1
0
Fork 0
mirror of https://github.com/opentx/opentx.git synced 2025-07-15 12:25: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

@ -98,7 +98,7 @@ void AppDebugMessageHandler::messageHandler(QtMsgType type, const QMessageLogCon
qSetMessagePattern(msgPattern); qSetMessagePattern(msgPattern);
if (!m_defaultHandler || receivers(SIGNAL(messageOutput(quint8, const QString &, const QMessageLogContext &)))) { if (!m_defaultHandler || receivers(SIGNAL(messageOutput(quint8, const QString &)))) {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
msgPattern = qFormatLogMessage(type, context, msg); msgPattern = qFormatLogMessage(type, context, msg);
#else #else
@ -111,7 +111,7 @@ void AppDebugMessageHandler::messageHandler(QtMsgType type, const QMessageLogCon
} }
#endif #endif
emit messageOutput(lvl, msgPattern, context); emit messageOutput(lvl, msgPattern);
} }
// if (QThread::currentThread() == qApp->thread()) // gui thread // if (QThread::currentThread() == qApp->thread()) // gui thread

View file

@ -127,7 +127,7 @@ class AppDebugMessageHandler : public QObject
bool m_showFunctionDeclarations; bool m_showFunctionDeclarations;
signals: signals:
void messageOutput(quint8 level, const QString & msg, const QMessageLogContext & context); void messageOutput(quint8 level, const QString & msg);
}; };
// Message handler which is installed using qInstallMessageHandler. This needs to be global. // Message handler which is installed using qInstallMessageHandler. This needs to be global.

View file

@ -12,6 +12,7 @@
<file>images/track.png</file> <file>images/track.png</file>
<file>images/track0.png</file> <file>images/track0.png</file>
<file>images/taranison.png</file> <file>images/taranison.png</file>
<file>images/simulator/icons/svg/arrow_click.svg</file>
<file>images/simulator/icons/svg/camera.svg</file> <file>images/simulator/icons/svg/camera.svg</file>
<file>images/simulator/icons/svg/camera-active.svg</file> <file>images/simulator/icons/svg/camera-active.svg</file>
<file>images/simulator/icons/svg/console.svg</file> <file>images/simulator/icons/svg/console.svg</file>
@ -32,6 +33,7 @@
<file>images/simulator/icons/svg/joystick_settings.svg</file> <file>images/simulator/icons/svg/joystick_settings.svg</file>
<file>images/simulator/icons/svg/joystick_settings-active.svg</file> <file>images/simulator/icons/svg/joystick_settings-active.svg</file>
<file>images/simulator/icons/svg/line_horiz.svg</file> <file>images/simulator/icons/svg/line_horiz.svg</file>
<file>images/simulator/icons/svg/mouse.svg</file>
<file>images/simulator/icons/svg/toggle_lock.svg</file> <file>images/simulator/icons/svg/toggle_lock.svg</file>
<file>images/simulator/icons/svg/toggle_lock-on.svg</file> <file>images/simulator/icons/svg/toggle_lock-on.svg</file>
<file>images/simulator/icons/svg/radio_outputs.svg</file> <file>images/simulator/icons/svg/radio_outputs.svg</file>

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="64" height="64" id="svg3007">
<defs id="defs3009" />
<metadata id="metadata3012">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g id="layer1">
<g transform="matrix(0.64592419,0,0,0.64592419,3.7118734,3.509355)" id="g4">
<g id="g6">
<path d="M 49.634152,27.060548 V 2.7303987 c 0,-1.6220105 -1.31026,-2.94910955 -2.911691,-2.94910955 -1.60143,0 -2.911692,1.32709905 -2.911692,2.94910955 V 27.060548 c 0,1.62201 1.310262,2.94911 2.911692,2.94911 1.601431,0 2.911691,-1.3271 2.911691,-2.94911 z" id="path8" />
<path d="M 2.3191619,50.211055 H 26.340619 c 1.60143,0 2.911691,-1.3271 2.911691,-2.94911 0,-1.62201 -1.310261,-2.94911 -2.911691,-2.94911 H 2.3191619 c -1.60142995,0 -2.91169115,1.3271 -2.91169115,2.94911 0,1.62201 1.3102612,2.94911 2.91169115,2.94911 z" id="path10" />
<path d="m 18.479051,18.803043 c -1.164676,1.179643 -1.164676,2.94911 0,4.128753 l 7.715983,7.815139 c 0.582339,0.589821 1.310262,0.884733 2.038184,0.884733 0.727924,0 1.455846,-0.294912 2.038185,-0.884733 1.164677,-1.179644 1.164677,-2.949109 0,-4.128753 L 22.55542,18.803043 c -1.019093,-1.032188 -2.911692,-1.032188 -4.076369,0 z" id="path12" />
<path d="m 70.16158,18.950499 -7.715985,7.815138 c -1.164674,1.179644 -1.164674,2.94911 0,4.128754 0.582339,0.589821 1.310263,0.884732 2.038185,0.884732 0.727924,0 1.455847,-0.294911 2.038185,-0.884732 l 7.715982,-7.81514 c 1.164677,-1.179644 1.164677,-2.949109 0,-4.128752 -1.019092,-1.03219 -2.911691,-1.03219 -4.076367,0 z" id="path14" />
<path d="m 26.04945,63.776956 -7.715984,7.81514 c -1.164675,1.179642 -1.164675,2.949108 0,4.128752 0.582339,0.589822 1.310262,0.884733 2.038185,0.884733 0.727924,0 1.455846,-0.294911 2.038184,-0.884733 l 7.715982,-7.815138 c 1.164677,-1.179644 1.164677,-2.94911 0,-4.128754 -1.164675,-1.179644 -2.91169,-1.179644 -4.076367,0 z" id="path16" />
<polygon points="85.3,77 64.5,56.2 81.3,50.5 39.5,38.7 51,80.8 56.7,64.1 77.5,84.8 " transform="matrix(0.87815057,0,0,0.88943545,9.8347839,11.453988)" id="polygon18" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" version="1.1" width="64" height="64" id="svg3007" inkscape:version="0.48.4 r9939" sodipodi:docname="mouse.svg">
<sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="640" inkscape:window-height="480" id="namedview8" showgrid="false" inkscape:zoom="3.6875" inkscape:cx="32" inkscape:cy="32" inkscape:current-layer="layer1" />
<defs id="defs3009" />
<metadata id="metadata3012">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g id="layer1">
<path d="m 32.627622,1.1648443 c -12.462898,0 -22.562142,8.7258932 -22.562142,19.4940177 v 22.27888 c 0,10.768123 10.099244,19.494018 22.562142,19.494018 12.462898,0 22.562144,-8.725895 22.562144,-19.494018 V 20.658862 C 55.189766,9.8907375 45.09052,1.1648443 32.627622,1.1648443 z M 49.817827,42.937742 c 0,8.168921 -7.735593,14.852585 -17.190205,14.852585 -9.454612,0 -17.190204,-6.683664 -17.190204,-14.852585 v -22.27888 c 0,-8.168921 7.735592,-14.8525846 17.190204,-14.8525846 9.454612,0 17.190205,6.6836636 17.190205,14.8525846 v 22.27888 z" id="path4" />
<path d="m 32.627622,9.2529782 c -3.116845,0 -5.565794,2.1994928 -5.565794,4.9988458 v 14.596624 c 0,2.799353 2.448949,4.998843 5.565794,4.998843 3.116843,0 5.565795,-2.19949 5.565795,-4.998843 V 14.251824 c 0,-2.599399 -2.448952,-4.9988458 -5.565795,-4.9988458 z" id="path6" inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -40,6 +40,7 @@ set(simulation_HDRS
radiouiaction.h radiouiaction.h
simulateduiwidget.h simulateduiwidget.h
# simulator.h # simulator.h
simulatorinterface.h
simulatormainwindow.h simulatormainwindow.h
simulatorstartupdialog.h simulatorstartupdialog.h
simulatorwidget.h simulatorwidget.h
@ -49,6 +50,7 @@ set(simulation_HDRS
widgets/lcdwidget.h widgets/lcdwidget.h
widgets/radiowidget.h widgets/radiowidget.h
widgets/radiofaderwidget.h widgets/radiofaderwidget.h
widgets/radiokeywidget.h
widgets/radioknobwidget.h widgets/radioknobwidget.h
widgets/radioswitchwidget.h widgets/radioswitchwidget.h
widgets/radiotrimwidget.h widgets/radiotrimwidget.h

View file

@ -36,20 +36,13 @@
extern AppData g; // ensure what "g" means extern AppData g; // ensure what "g" means
FilteredTextBuffer * DebugOutput::m_dataBufferDevice = Q_NULLPTR;
const quint16 DebugOutput::m_savedViewStateVersion = 1; const quint16 DebugOutput::m_savedViewStateVersion = 1;
void firmwareTraceCb(const char * text)
{
if (DebugOutput::m_dataBufferDevice) {
DebugOutput::m_dataBufferDevice->write(text);
}
}
DebugOutput::DebugOutput(QWidget * parent, SimulatorInterface *simulator): DebugOutput::DebugOutput(QWidget * parent, SimulatorInterface *simulator):
QWidget(parent), QWidget(parent),
ui(new Ui::DebugOutput), ui(new Ui::DebugOutput),
m_simulator(simulator), m_simulator(simulator),
m_dataBufferDevice(NULL),
m_radioProfileId(g.sessionId()), m_radioProfileId(g.sessionId()),
m_filterEnable(false), m_filterEnable(false),
m_filterExclude(false) m_filterExclude(false)
@ -101,33 +94,28 @@ DebugOutput::DebugOutput(QWidget * parent, SimulatorInterface *simulator):
connect(ui->actionToggleFilter, &QAction::toggled, this, &DebugOutput::onFilterToggled); connect(ui->actionToggleFilter, &QAction::toggled, this, &DebugOutput::onFilterToggled);
connect(ui->filterText, &QComboBox::currentTextChanged, this, &DebugOutput::onFilterTextChanged); connect(ui->filterText, &QComboBox::currentTextChanged, this, &DebugOutput::onFilterTextChanged);
if (AppDebugMessageHandler::instance()) { if (AppDebugMessageHandler::instance())
#if (QT_VERSION < QT_VERSION_CHECK(5, 3, 0)) // https://bugreports.qt.io/browse/QTBUG-36119
connect(AppDebugMessageHandler::instance(), SIGNAL(messageOutput(quint8,QString,QMessageLogContext)), this, SLOT(onAppDebugMessage(quint8,QString,QMessageLogContext)));
#else
connect(AppDebugMessageHandler::instance(), &AppDebugMessageHandler::messageOutput, this, &DebugOutput::onAppDebugMessage); connect(AppDebugMessageHandler::instance(), &AppDebugMessageHandler::messageOutput, this, &DebugOutput::onAppDebugMessage);
#endif
}
// send firmware TRACE events to our data collector // send firmware TRACE events to our data collector
m_simulator->installTraceHook(firmwareTraceCb); m_simulator->addTracebackDevice(m_dataBufferDevice);
} }
DebugOutput::~DebugOutput() DebugOutput::~DebugOutput()
{ {
m_simulator->installTraceHook(NULL);
saveState();
if (AppDebugMessageHandler::instance()) if (AppDebugMessageHandler::instance())
disconnect(AppDebugMessageHandler::instance(), 0, this, 0); disconnect(AppDebugMessageHandler::instance(), 0, this, 0);
if (m_dataBufferDevice) { if (m_dataBufferDevice) {
m_simulator->removeTracebackDevice(m_dataBufferDevice);
disconnect(m_dataBufferDevice, 0, this, 0); disconnect(m_dataBufferDevice, 0, this, 0);
disconnect(this, 0, m_dataBufferDevice, 0); disconnect(this, 0, m_dataBufferDevice, 0);
m_dataBufferDevice->deleteLater(); delete m_dataBufferDevice;
m_dataBufferDevice = Q_NULLPTR; m_dataBufferDevice = Q_NULLPTR;
} }
saveState();
delete ui; delete ui;
} }
@ -213,11 +201,10 @@ void DebugOutput::onDataBufferOverflow(const qint64 len)
} }
} }
void DebugOutput::onAppDebugMessage(quint8 level, const QString & msg, const QMessageLogContext & context) void DebugOutput::onAppDebugMessage(quint8 level, const QString & msg)
{ {
if (level > 0) { if (level > 0 && m_dataBufferDevice) {
firmwareTraceCb(qPrintable(msg)); m_dataBufferDevice->write(qPrintable(msg % "\n"));
firmwareTraceCb("\n");
} }
} }

View file

@ -60,20 +60,19 @@ class DebugOutput : public QWidget
static QRegularExpression makeRegEx(const QString & input, bool * isExlusive = NULL); static QRegularExpression makeRegEx(const QString & input, bool * isExlusive = NULL);
static FilteredTextBuffer * m_dataBufferDevice;
signals: signals:
void filterExprChanged(const QRegularExpression & expr); void filterExprChanged(const QRegularExpression & expr);
void filterEnabledChanged(const bool enabled); void filterEnabledChanged(const bool enabled);
void filterExclusiveChanged(const bool exlusive); void filterExclusiveChanged(const bool exlusive);
void filterChanged(bool enable, bool exclusive, const QRegularExpression & expr); void filterChanged(bool enable, bool exclusive, const QRegularExpression & expr);
void tracebackDeviceChange(QIODevice * device);
protected slots: protected slots:
void saveState(); void saveState();
void restoreState(); void restoreState();
void processBytesReceived(); void processBytesReceived();
void onDataBufferOverflow(const qint64 len); void onDataBufferOverflow(const qint64 len);
void onAppDebugMessage(quint8 level, const QString & msg, const QMessageLogContext & context); void onAppDebugMessage(quint8 level, const QString & msg);
void onFilterStateChanged(); void onFilterStateChanged();
void onFilterTextChanged(const QString &); void onFilterTextChanged(const QString &);
void onFilterToggled(bool enable); void onFilterToggled(bool enable);
@ -85,6 +84,7 @@ class DebugOutput : public QWidget
protected: protected:
Ui::DebugOutput * ui; Ui::DebugOutput * ui;
SimulatorInterface * m_simulator; SimulatorInterface * m_simulator;
FilteredTextBuffer * m_dataBufferDevice;
int m_radioProfileId; int m_radioProfileId;
bool m_filterEnable; bool m_filterEnable;
bool m_filterExclude; bool m_filterExclude;

View file

@ -26,39 +26,32 @@
#include "eeprominterface.h" #include "eeprominterface.h"
#include "radiodata.h" #include "radiodata.h"
#include "simulator.h" #include "simulator.h"
#include "simulatorinterface.h"
extern AppData g; // ensure what "g" means extern AppData g; // ensure what "g" means
const int RadioOutputsWidget::m_dataUpdateFreqDefault = 10; // ms
const quint16 RadioOutputsWidget::m_savedViewStateVersion = 1; const quint16 RadioOutputsWidget::m_savedViewStateVersion = 1;
RadioOutputsWidget::RadioOutputsWidget(SimulatorInterface * simulator, Firmware * firmware, QWidget *parent) : RadioOutputsWidget::RadioOutputsWidget(SimulatorInterface * simulator, Firmware * firmware, QWidget *parent) :
QWidget(parent), QWidget(parent),
m_simulator(simulator), m_simulator(simulator),
m_firmware(firmware), m_firmware(firmware),
m_tmrUpdateData(new QTimer),
m_radioProfileId(g.sessionId()), m_radioProfileId(g.sessionId()),
m_lastFlightPhase(-1),
m_started(false),
ui(new Ui::RadioOutputsWidget) ui(new Ui::RadioOutputsWidget)
{ {
ui->setupUi(this); ui->setupUi(this);
restoreState(); restoreState();
m_dataUpdateFreq = m_dataUpdateFreqDefault; connect(m_simulator, &SimulatorInterface::channelOutValueChange, this, &RadioOutputsWidget::onChannelOutValueChange);
m_tmrUpdateData->setInterval(m_dataUpdateFreq); connect(m_simulator, &SimulatorInterface::virtualSwValueChange, this, &RadioOutputsWidget::onVirtSwValueChange);
connect(m_simulator, &SimulatorInterface::gVarValueChange, this, &RadioOutputsWidget::onGVarValueChange);
connect(m_tmrUpdateData, &QTimer::timeout, this, &RadioOutputsWidget::setValues); connect(m_simulator, &SimulatorInterface::phaseChanged, this, &RadioOutputsWidget::onPhaseChanged);
} }
RadioOutputsWidget::~RadioOutputsWidget() RadioOutputsWidget::~RadioOutputsWidget()
{ {
stop(); //stop();
saveState(); saveState();
if (m_tmrUpdateData)
delete m_tmrUpdateData;
delete ui; delete ui;
} }
@ -74,36 +67,20 @@ void RadioOutputsWidget::changeEvent(QEvent *e)
} }
} }
void RadioOutputsWidget::showEvent(QShowEvent * event)
{
if (m_started)
m_tmrUpdateData->start();
}
void RadioOutputsWidget::hideEvent(QHideEvent * event)
{
m_tmrUpdateData->stop();
}
void RadioOutputsWidget::start() void RadioOutputsWidget::start()
{ {
setupChannelsDisplay(); setupChannelsDisplay();
setupGVarsDisplay(); setupGVarsDisplay();
setupLsDisplay(); setupLsDisplay();
m_lastFlightPhase = -1;
m_tmrUpdateData->start();
m_started = true;
} }
void RadioOutputsWidget::stop() //void RadioOutputsWidget::stop()
{ //{
m_tmrUpdateData->stop(); //}
m_started = false;
}
void RadioOutputsWidget::restart() void RadioOutputsWidget::restart()
{ {
stop(); //stop();
start(); start();
} }
@ -139,7 +116,6 @@ void RadioOutputsWidget::restoreState()
ui->splitter->restoreState(splitterState); ui->splitter->restoreState(splitterState);
} }
void RadioOutputsWidget::setupChannelsDisplay() void RadioOutputsWidget::setupChannelsDisplay()
{ {
int outputs = std::min(32, m_firmware->getCapability(Capability(Outputs))); int outputs = std::min(32, m_firmware->getCapability(Capability(Outputs)));
@ -297,68 +273,73 @@ QWidget * RadioOutputsWidget::createLogicalSwitch(QWidget * parent, int switchNo
return swtch; return swtch;
} }
// Read various values from firmware simulator and populate values in this UI void RadioOutputsWidget::onChannelOutValueChange(quint8 index, qint32 value)
void RadioOutputsWidget::setValues()
{ {
static TxOutputs prevOutputs = TxOutputs(); if (m_channelsMap.contains(index)) {
int currentPhase; QPair<QLabel *, QSlider *> ch = m_channelsMap.value(index);
TxOutputs outputs; ch.first->setText(QString("%1%").arg(calcRESXto100(value)));
ch.second->setValue(qMin(1024, qMax(-1024, value)));
}
//qDebug() << index << value;
}
void RadioOutputsWidget::onVirtSwValueChange(quint8 index, qint32 value)
{
if (!m_logicSwitchMap.contains(index))
return;
QLabel * ls = m_logicSwitchMap.value(index);
ls->setBackgroundRole(value ? QPalette::Dark : QPalette::Background);
ls->setForegroundRole(value ? QPalette::BrightText : QPalette::WindowText);
ls->setFrameShadow(value ? QFrame::Sunken : QFrame::Raised);
QFont font = ls->font();
font.setBold((bool)value);
ls->setFont(font);
//qDebug() << index << value;
}
void RadioOutputsWidget::onGVarValueChange(quint8 index, qint32 value)
{
if (!m_globalVarsMap.contains(index))
return;
QHash<int, QLabel *> fmMap = m_globalVarsMap.value(index);
SimulatorInterface::gVarMode_t gv(value);
if (fmMap.contains(gv.mode)) {
QLabel * lbl = fmMap.value(gv.mode);
if (lbl)
lbl->setText(QString::number(gv.value));
}
//qDebug() << index << value << gv.mode << gv.value;
}
void RadioOutputsWidget::onPhaseChanged(qint32 phase, const QString &)
{
QPalette::ColorRole fgrole, bgrole;
QLabel * lbl;
QFont font; QFont font;
m_simulator->getValues(outputs);
currentPhase = m_simulator->getPhase();
if (ui->channelsWidget->isVisible()) {
QHash<int, QPair<QLabel *, QSlider *> >::const_iterator ch;
for (ch = m_channelsMap.constBegin(); ch != m_channelsMap.constEnd(); ++ch) {
if (ch.key() >= CPN_MAX_CHNOUT)
continue;
ch.value().first->setText(QString("%1%").arg(calcRESXto100(outputs.chans[ch.key()])));
ch.value().second->setValue(qMin(1024, qMax(-1024, outputs.chans[ch.key()])));
}
}
if (ui->logicalSwitchesWidget->isVisible()) {
QHash<int, QLabel* >::const_iterator ls;
for (ls = m_logicSwitchMap.constBegin(); ls != m_logicSwitchMap.constEnd(); ++ls) {
if (ls.key() >= CPN_MAX_LOGICAL_SWITCHES || (prevOutputs.vsw[ls.key()] == outputs.vsw[ls.key()] && m_lastFlightPhase > -1))
continue;
ls.value()->setBackgroundRole(outputs.vsw[ls.key()] ? QPalette::Dark : QPalette::Background);
ls.value()->setForegroundRole(outputs.vsw[ls.key()] ? QPalette::BrightText : QPalette::WindowText);
ls.value()->setFrameShadow(outputs.vsw[ls.key()] ? QFrame::Sunken : QFrame::Raised);
font = ls.value()->font();
font.setBold(outputs.vsw[ls.key()]);
ls.value()->setFont(font);
prevOutputs.vsw[ls.key()] = outputs.vsw[ls.key()];
}
}
if (ui->globalVarsWidget->isVisible()) {
QPalette::ColorRole bgrole;
QHash<int, QHash<int, QLabel *> >::const_iterator gv; QHash<int, QHash<int, QLabel *> >::const_iterator gv;
QHash<int, QLabel *>::const_iterator fm; QHash<int, QLabel *>::const_iterator fm;
for (gv = m_globalVarsMap.constBegin(); gv != m_globalVarsMap.constEnd(); ++gv) {
if (gv.key() >= CPN_MAX_GVARS)
continue;
for (fm = gv.value().constBegin(); fm != gv.value().constEnd(); ++fm) {
if (fm.key() >= CPN_MAX_FLIGHT_MODES)
continue;
if (currentPhase != m_lastFlightPhase || prevOutputs.gvars[fm.key()][gv.key()] != outputs.gvars[fm.key()][gv.key()]) {
if (fm.key() == currentPhase)
bgrole = QPalette::Dark;
else
bgrole = ((gv.key() % 2) ? QPalette::Background : QPalette::AlternateBase);
fm.value()->setBackgroundRole(bgrole);
fm.value()->setForegroundRole(fm.key() == currentPhase ? QPalette::BrightText : QPalette::WindowText);
font = fm.value()->font();
font.setBold(fm.key() == currentPhase);
fm.value()->setFont(font);
fm.value()->setText(QString::number(outputs.gvars[fm.key()][gv.key()]));
prevOutputs.gvars[fm.key()][gv.key()] = outputs.gvars[fm.key()][gv.key()];
}
}
}
}
m_lastFlightPhase = currentPhase; for (gv = m_globalVarsMap.constBegin(); gv != m_globalVarsMap.constEnd(); ++gv) {
for (fm = gv.value().constBegin(); fm != gv.value().constEnd(); ++fm) {
lbl = fm.value();
font = lbl->font();
if (fm.key() == phase) {
fgrole = QPalette::BrightText;
bgrole = QPalette::Dark;
font.setBold(true);
}
else {
fgrole = QPalette::WindowText;
bgrole = ((gv.key() % 2) ? QPalette::Background : QPalette::AlternateBase);
font.setBold(false);
}
lbl->setForegroundRole(fgrole);
lbl->setBackgroundRole(bgrole);
lbl->setFont(font);
}
}
//qDebug() << phase;
} }

View file

@ -22,6 +22,7 @@
#define RADIOOUTPUTSWIDGET_H #define RADIOOUTPUTSWIDGET_H
#include "simulator.h" #include "simulator.h"
#include "simulatorinterface.h"
#include <QTimer> #include <QTimer>
#include <QWidget> #include <QWidget>
@ -31,7 +32,7 @@ class RadioOutputsWidget;
} }
class Firmware; class Firmware;
class SimulatorInterface; //class SimulatorInterface;
class QFrame; class QFrame;
class QLabel; class QLabel;
@ -47,16 +48,18 @@ class RadioOutputsWidget : public QWidget
explicit RadioOutputsWidget(SimulatorInterface * simulator, Firmware * firmware, QWidget * parent = 0); explicit RadioOutputsWidget(SimulatorInterface * simulator, Firmware * firmware, QWidget * parent = 0);
~RadioOutputsWidget(); ~RadioOutputsWidget();
public slots:
void start(); void start();
void stop(); //void stop();
void restart(); void restart();
protected slots: protected slots:
void saveState(); void saveState();
void restoreState(); void restoreState();
void setValues(); void onChannelOutValueChange(quint8 index, qint32 value);
void showEvent(QShowEvent *event); void onVirtSwValueChange(quint8 index, qint32 value);
void hideEvent(QHideEvent *event); void onGVarValueChange(quint8 index, qint32 value);
void onPhaseChanged(qint32 phase, const QString &);
protected: protected:
void changeEvent(QEvent *e); void changeEvent(QEvent *e);
@ -67,7 +70,6 @@ class RadioOutputsWidget : public QWidget
SimulatorInterface * m_simulator; SimulatorInterface * m_simulator;
Firmware * m_firmware; Firmware * m_firmware;
QTimer * m_tmrUpdateData;
QHash<int, QPair<QLabel *, QSlider *> > m_channelsMap; // m_channelsMap[chanIndex] = {QLabel*, QSlider*} QHash<int, QPair<QLabel *, QSlider *> > m_channelsMap; // m_channelsMap[chanIndex] = {QLabel*, QSlider*}
QHash<int, QLabel *> m_logicSwitchMap; // m_logicSwitchMap[lsIndex] = QLabel* QHash<int, QLabel *> m_logicSwitchMap; // m_logicSwitchMap[lsIndex] = QLabel*
@ -75,10 +77,7 @@ class RadioOutputsWidget : public QWidget
int m_radioProfileId; int m_radioProfileId;
int m_dataUpdateFreq; int m_dataUpdateFreq;
int m_lastFlightPhase;
bool m_started;
const static int m_dataUpdateFreqDefault;
const static quint16 m_savedViewStateVersion; const static quint16 m_savedViewStateVersion;
private: private:

View file

@ -37,12 +37,12 @@ class RadioUiAction : public QObject
/* /*
* @param index Typically this is the hardware array index corresponding to button on current radio model, * @param index Typically this is the hardware array index corresponding to button on current radio model,
* but it could be anything. Use -1 or any other negative value for non-hardware indices. * but it could be anything. Use -1 or any other negative value for non-hardware indices.
* @param key An optional Qt:Key code for shortcut. * @param key An optional Qt:Key code for shortcut (zero for none).
* @param parent Parent widget, required for handling keyboard events. * @param parent Parent widget, required for handling keyboard events.
* @param text Optional title for this action. The text and description are currently used in generated help text. * @param text Optional title for this action. The text and description are currently used in generated help text.
* @param descript Optional longer description text for this action. * @param descript Optional longer description text for this action.
*/ */
RadioUiAction(int index = -1, int key = 0, QWidget * parent = NULL, const QString &text = "", const QString &descript = ""): RadioUiAction(int index = -1, int key = 0, const QString &text = "", const QString &descript = "", QWidget * parent = NULL):
m_hwIndex(index), m_hwIndex(index),
m_active(false), m_active(false),
m_keys(QList<int>()), m_keys(QList<int>()),
@ -50,37 +50,51 @@ class RadioUiAction : public QObject
m_description(descript), m_description(descript),
m_parent(parent) m_parent(parent)
{ {
if (key) addKey(key);
m_keys.append(key);
init();
} }
/* /*
* @param keys QList of Qt:Key codes to use as shortcuts. * @param keys QList of Qt:Key codes to use as shortcuts.
* [See above for other params.] * [See above for other params.]
*/ */
RadioUiAction(int index, QList<int> keys, QWidget * parent = NULL, const QString &text = "", const QString &descript = ""): RadioUiAction(int index, QList<int> keys, const QString &text = "", const QString &descript = "", QWidget * parent = NULL):
m_hwIndex(index), RadioUiAction(index, 0, text, descript, parent)
m_active(false),
m_keys(keys),
m_text(text),
m_description(descript),
m_parent(parent)
{ {
init(); addKeys(keys);
} }
void init() void addKey(const int & key)
{ {
if (m_keys.size()) if (key > 0 && !m_keys.contains(key)) {
m_keys.append(key);
addShortcut(); addShortcut();
} }
}
void addKeys(const QList<int> & keys)
{
foreach (int key, keys)
addKey(key);
}
void addShortcut() void addShortcut()
{ {
if (m_parent) if (m_parent) {
m_parent->removeEventFilter(this);
if (m_keys.size())
m_parent->installEventFilter(this); m_parent->installEventFilter(this);
} }
}
// override
void setParent(QObject * parent)
{
QObject::setParent(parent);
QWidget * w = qobject_cast<QWidget *>(parent);
if (w && w != m_parent) {
m_parent = w;
addShortcut();
}
}
void setDescription(const QString & description) { m_description = description; } void setDescription(const QString & description) { m_description = description; }
void setText(QPair<QString, QString> name_descript) { setText(name_descript.first, name_descript.second); } void setText(QPair<QString, QString> name_descript) { setText(name_descript.first, name_descript.second); }

View file

@ -22,8 +22,13 @@
#include "eeprominterface.h" #include "eeprominterface.h"
#include "lcdwidget.h" #include "lcdwidget.h"
#include "radiouiaction.h" #include "radiouiaction.h"
#include "radiokeywidget.h"
#include "simulatorinterface.h" #include "simulatorinterface.h"
//#define FLASH_DURATION 10
//#define CBEEP_ON "QLabel { background-color: #FF364E }"
//#define CBEEP_OFF "QLabel { }"
SimulatedUIWidget::SimulatedUIWidget(SimulatorInterface * simulator, QWidget * parent) : SimulatedUIWidget::SimulatedUIWidget(SimulatorInterface * simulator, QWidget * parent) :
QWidget(parent), QWidget(parent),
m_simulator(simulator), m_simulator(simulator),
@ -31,16 +36,18 @@ SimulatedUIWidget::SimulatedUIWidget(SimulatorInterface * simulator, QWidget * p
m_lcd(NULL), m_lcd(NULL),
m_scrollUpAction(NULL), m_scrollUpAction(NULL),
m_scrollDnAction(NULL), m_scrollDnAction(NULL),
m_rotEncClickAction(NULL), m_mouseMidClickAction(NULL),
m_screenshotAction(NULL),
m_board(getCurrentBoard()), m_board(getCurrentBoard()),
m_backLight(0), m_backLight(0),
m_lightOn(false),
m_beepShow(0), m_beepShow(0),
m_beepVal(0) m_beepVal(0)
{ {
m_rotEncClickAction = addRadioUiAction(-1, 0, tr("Rotary encoder click")); m_screenshotAction = new RadioUiAction(-1, Qt::Key_Print);
m_screenshotAction = addRadioUiAction(-1, Qt::Key_Print, tr("Take Screenshot"));
connect(m_screenshotAction, static_cast<void (RadioUiAction::*)(void)>(&RadioUiAction::pushed), this, &SimulatedUIWidget::captureScreenshot); connect(m_screenshotAction, static_cast<void (RadioUiAction::*)(void)>(&RadioUiAction::pushed), this, &SimulatedUIWidget::captureScreenshot);
connect(m_simulator, &SimulatorInterface::lcdChange, this, &SimulatedUIWidget::onLcdChange);
connect(this, &SimulatedUIWidget::simulatorWheelEvent, m_simulator, &SimulatorInterface::rotaryEncoderEvent);
} }
SimulatedUIWidget::~SimulatedUIWidget() SimulatedUIWidget::~SimulatedUIWidget()
@ -49,28 +56,44 @@ SimulatedUIWidget::~SimulatedUIWidget()
if (act) if (act)
delete act; delete act;
} }
foreach (RadioWidget * w, m_widgets) {
if (w)
delete w;
}
} }
RadioUiAction * SimulatedUIWidget::addRadioUiAction(RadioUiAction * act) RadioWidget * SimulatedUIWidget::addRadioWidget(RadioWidget * widget)
{ {
if (act) { if (widget && !m_widgets.contains(widget)) {
m_widgets.append(widget);
// TODO : connect to actions instead
connect(widget, &RadioWidget::valueChange, this, &SimulatedUIWidget::controlValueChange);
if (widget->getAction())
addRadioAction(widget->getAction());
}
return widget;
}
RadioUiAction * SimulatedUIWidget::addRadioAction(RadioUiAction * act)
{
if (act && !m_actions.contains(act)) {
act->setParent(m_parent);
m_actions.append(act); m_actions.append(act);
if (!act->getText().isEmpty() && !act->getDescription().isEmpty())
m_keymapHelp.append(keymapHelp_t(act->getText(), act->getDescription()));
} }
return act; return act;
} }
RadioUiAction * SimulatedUIWidget::addRadioUiAction(int index, int key, const QString & text, const QString & descript) QVector<Simulator::keymapHelp_t> SimulatedUIWidget::getKeymapHelp() const
{ {
return addRadioUiAction(new RadioUiAction(index, key, m_parent, text, descript)); QVector<Simulator::keymapHelp_t> keymapHelp;
} foreach (RadioUiAction * act, m_actions) {
if (act && !act->getText().isEmpty())
RadioUiAction * SimulatedUIWidget::addRadioUiAction(int index, QList<int> keys, const QString & text, const QString & descript) keymapHelp.append(Simulator::keymapHelp_t(act->getText(), act->getDescription()));
{ }
return addRadioUiAction(new RadioUiAction(index, keys, m_parent, text, descript)); return keymapHelp;
} }
// static
QPolygon SimulatedUIWidget::polyArc(int ctrX, int ctrY, int radius, int startAngle, int endAngle, int step) QPolygon SimulatedUIWidget::polyArc(int ctrX, int ctrY, int radius, int startAngle, int endAngle, int step)
{ {
QPolygon polygon; QPolygon polygon;
@ -83,20 +106,10 @@ QPolygon SimulatedUIWidget::polyArc(int ctrX, int ctrY, int radius, int startAng
return polygon; return polygon;
} }
/* TODO : beep indicator
void SimulatedUIWidget::updateUi() void SimulatedUIWidget::updateUi()
{ {
//static quint32 loop = 0; //static quint32 loop = 0;
if (m_lcd->isVisible()) {
bool lightEnable;
if (m_simulator->lcdChanged(lightEnable)) {
m_lcd->onLcdChanged(lightEnable);
if (m_lightOn != lightEnable) {
setLightOn(lightEnable);
m_lightOn = lightEnable;
}
}
}
/* TODO : beep indicator
if (!(loop % 5)) { if (!(loop % 5)) {
TxOutputs outputs; TxOutputs outputs;
simulator->getValues(outputs); simulator->getValues(outputs);
@ -111,7 +124,16 @@ void SimulatedUIWidget::updateUi()
beepShow--; beepShow--;
} }
ui->label_beep->setStyleSheet(beepShow ? CBEEP_ON : CBEEP_OFF); ui->label_beep->setStyleSheet(beepShow ? CBEEP_ON : CBEEP_OFF);
} */ }
} */
void SimulatedUIWidget::onLcdChange(bool backlightEnable)
{
if (!m_lcd || !m_lcd->isVisible())
return;
m_lcd->onLcdChanged(backlightEnable);
setLightOn(backlightEnable);
} }
void SimulatedUIWidget::captureScreenshot() void SimulatedUIWidget::captureScreenshot()
@ -136,32 +158,27 @@ void SimulatedUIWidget::captureScreenshot()
m_lcd->makeScreenshot(fileName); m_lcd->makeScreenshot(fileName);
} }
// steps can be negative or positive to determine direction (negative is UP/RIGHT scroll)
void SimulatedUIWidget::simulatorWheelEvent(qint8 steps)
{
m_simulator->wheelEvent(steps);
}
void SimulatedUIWidget::wheelEvent(QWheelEvent * event) void SimulatedUIWidget::wheelEvent(QWheelEvent * event)
{ {
if (event->angleDelta().isNull()) if (event->angleDelta().isNull())
return; return;
// steps can be negative or positive to determine direction (negative is UP/LEFT scroll)
QPoint numSteps = event->angleDelta() / 8 / 15 * -1; // one step per 15deg QPoint numSteps = event->angleDelta() / 8 / 15 * -1; // one step per 15deg
simulatorWheelEvent(numSteps.y()); emit simulatorWheelEvent(numSteps.y());
} }
void SimulatedUIWidget::mousePressEvent(QMouseEvent * event) void SimulatedUIWidget::mousePressEvent(QMouseEvent * event)
{ {
if (event->button() == Qt::MidButton && m_rotEncClickAction) if (event->button() == Qt::MidButton && m_mouseMidClickAction)
m_rotEncClickAction->trigger(true); m_mouseMidClickAction->trigger(true);
else else
event->ignore(); event->ignore();
} }
void SimulatedUIWidget::mouseReleaseEvent(QMouseEvent * event) void SimulatedUIWidget::mouseReleaseEvent(QMouseEvent * event)
{ {
if (event->button() == Qt::MidButton && m_rotEncClickAction) if (event->button() == Qt::MidButton && m_mouseMidClickAction)
m_rotEncClickAction->trigger(false); m_mouseMidClickAction->trigger(false);
else else
event->ignore(); event->ignore();
} }
@ -184,13 +201,19 @@ void SimulatedUIWidget::setLcd(LcdWidget * lcd)
void SimulatedUIWidget::connectScrollActions() void SimulatedUIWidget::connectScrollActions()
{ {
if (m_scrollUpAction) {
addRadioAction(m_scrollUpAction);
connect(m_scrollUpAction, static_cast<void (RadioUiAction::*)(void)>(&RadioUiAction::pushed), [this](void) { connect(m_scrollUpAction, static_cast<void (RadioUiAction::*)(void)>(&RadioUiAction::pushed), [this](void) {
this->simulatorWheelEvent(-1); emit simulatorWheelEvent(-1);
m_scrollUpAction->toggle(false); m_scrollUpAction->toggle(false);
}); });
}
if (m_scrollDnAction) {
addRadioAction(m_scrollDnAction);
connect(m_scrollDnAction, static_cast<void (RadioUiAction::*)(void)>(&RadioUiAction::pushed), [this](void) { connect(m_scrollDnAction, static_cast<void (RadioUiAction::*)(void)>(&RadioUiAction::pushed), [this](void) {
simulatorWheelEvent(1); emit simulatorWheelEvent(1);
m_scrollDnAction->toggle(false); m_scrollDnAction->toggle(false);
}); });
}
} }

View file

@ -23,17 +23,18 @@
#include "boards.h" #include "boards.h"
#include "constants.h" #include "constants.h"
#include "radiowidget.h"
#include "simulator.h" #include "simulator.h"
#include "simulator_strings.h"
#include <QWidget> #include <QWidget>
#include <QMouseEvent> #include <QMouseEvent>
class SimulatorInterface; class SimulatorInterface;
class LcdWidget; class LcdWidget;
class RadioKeyWidget;
class RadioUiAction; class RadioUiAction;
using namespace Simulator;
/* /*
* This is a base class for the main hardware-specific radio user interface, including LCD screen and navigation buttons/widgets. * This is a base class for the main hardware-specific radio user interface, including LCD screen and navigation buttons/widgets.
* It is responsible for hanlding all interactions with this part of the simulation (vs. common radio widgets like sticks/switches/knobs). * It is responsible for hanlding all interactions with this part of the simulation (vs. common radio widgets like sticks/switches/knobs).
@ -52,51 +53,51 @@ class SimulatedUIWidget : public QWidget
~SimulatedUIWidget(); ~SimulatedUIWidget();
RadioUiAction * addRadioUiAction(RadioUiAction * act); RadioWidget * addRadioWidget(RadioWidget * keyWidget);
RadioUiAction * addRadioUiAction(int index = -1, int key = 0, const QString &text = "", const QString &descript = ""); RadioUiAction * addRadioAction(RadioUiAction * act);
RadioUiAction * addRadioUiAction(int index, QList<int> keys, const QString &text = "", const QString &descript = "");
QVector<keymapHelp_t> * getKeymapHelp() { return &m_keymapHelp; } QVector<Simulator::keymapHelp_t> getKeymapHelp() const;
QList<RadioUiAction *> getActions() const { return m_actions; }
RadioUiAction * getRotEncAction() const { return m_rotEncClickAction; }
QPolygon polyArc(int ctrX, int ctrY, int radius, int startAngle = 0, int endAngle = 360, int step = 10); static QPolygon polyArc(int ctrX, int ctrY, int radius, int startAngle = 0, int endAngle = 360, int step = 10);
public slots: public slots:
void updateUi();
void captureScreenshot(); void captureScreenshot();
void simulatorWheelEvent(qint8 steps);
void wheelEvent(QWheelEvent *event); void wheelEvent(QWheelEvent *event);
void mousePressEvent(QMouseEvent *event); void mousePressEvent(QMouseEvent *event);
void mouseReleaseEvent(QMouseEvent *event); void mouseReleaseEvent(QMouseEvent *event);
protected: signals:
void controlValueChange(RadioWidget::RadioWidgetType type, int index, int value);
void customStyleRequest(const QString & style);
void simulatorWheelEvent(qint8 steps);
protected slots:
void setLcd(LcdWidget * lcd); void setLcd(LcdWidget * lcd);
void connectScrollActions(); void connectScrollActions();
void onLcdChange(bool backlightEnable);
virtual void setLightOn(bool enable) { } virtual void setLightOn(bool enable) { }
protected:
SimulatorInterface * m_simulator; SimulatorInterface * m_simulator;
QWidget * m_parent; QWidget * m_parent;
LcdWidget * m_lcd; LcdWidget * m_lcd;
QVector<QColor> m_backlightColors; QVector<QColor> m_backlightColors;
QVector<keymapHelp_t> m_keymapHelp;
QList<RadioUiAction *> m_actions; QList<RadioUiAction *> m_actions;
QList<RadioWidget *> m_widgets;
RadioUiAction * m_scrollUpAction; RadioUiAction * m_scrollUpAction;
RadioUiAction * m_scrollDnAction; RadioUiAction * m_scrollDnAction;
RadioUiAction * m_rotEncClickAction; RadioUiAction * m_mouseMidClickAction;
RadioUiAction * m_screenshotAction; RadioUiAction * m_screenshotAction;
Board::Type m_board; Board::Type m_board;
unsigned int m_backLight; unsigned int m_backLight;
bool m_lightOn;
int m_beepShow; int m_beepShow;
int m_beepVal; int m_beepVal;
signals:
void customStyleRequest(const QString & style);
}; };

View file

@ -20,6 +20,7 @@
#include "simulateduiwidget.h" #include "simulateduiwidget.h"
#include "ui_simulateduiwidget9X.h" #include "ui_simulateduiwidget9X.h"
#include "eeprominterface.h"
SimulatedUIWidget9X::SimulatedUIWidget9X(SimulatorInterface * simulator, QWidget * parent): SimulatedUIWidget9X::SimulatedUIWidget9X(SimulatorInterface * simulator, QWidget * parent):
SimulatedUIWidget(simulator, parent), SimulatedUIWidget(simulator, parent),
@ -30,39 +31,49 @@ SimulatedUIWidget9X::SimulatedUIWidget9X(SimulatorInterface * simulator, QWidget
ui->setupUi(this); ui->setupUi(this);
bool hasRotEnc = getCurrentFirmware()->getCapability(Capability::RotaryEncoders);
// add actions in order of appearance on the help menu // add actions in order of appearance on the help menu
int x = 68, y = 91, oR = 63; int x = 68, y = 91, oR = 63;
polygon << QPoint(x, y) << polyArc(x, y, oR, -45, 45); polygon << QPoint(x, y) << polyArc(x, y, oR, -45, 45);
act = addRadioUiAction(3, QList<int>() << Qt::Key_Up << Qt::Key_PageUp, tr("UP/PG-UP"), tr("[ UP ]")); act = new RadioUiAction(3, QList<int>() << Qt::Key_Up << Qt::Key_PageUp, tr(SIMU_STR_HLP_KEYS_GO_UP) % (hasRotEnc ? "" : tr("|" SIMU_STR_HLP_MOUSE_UP)), tr(SIMU_STR_HLP_ACT_UP));
ui->leftbuttons->addArea(polygon, "9X/9xcursup.png", act); addRadioWidget(ui->leftbuttons->addArea(polygon, "9X/9xcursup.png", act));
polygon.clear(); polygon.clear();
polygon << QPoint(x, y) << polyArc(x, y, oR, 135, 225); polygon << QPoint(x, y) << polyArc(x, y, oR, 135, 225);
act = addRadioUiAction(2, QList<int>() << Qt::Key_Down << Qt::Key_PageDown, tr("DN/PG-DN"), tr("[ DN ]")); act = new RadioUiAction(2, QList<int>() << Qt::Key_Down << Qt::Key_PageDown, tr(SIMU_STR_HLP_KEYS_GO_DN) % (hasRotEnc ? "" : tr("|" SIMU_STR_HLP_MOUSE_DN)), tr(SIMU_STR_HLP_ACT_DN));
ui->leftbuttons->addArea(polygon, "9X/9xcursdown.png", act); addRadioWidget(ui->leftbuttons->addArea(polygon, "9X/9xcursdown.png", act));
polygon.clear(); polygon.clear();
polygon << QPoint(x, y) << polyArc(x, y, oR, 45, 135); polygon << QPoint(x, y) << polyArc(x, y, oR, 45, 135);
act = addRadioUiAction(4, QList<int>() << Qt::Key_Right << Qt::Key_Plus << Qt::Key_Equal, tr("RIGHT/+"), tr("[ + ]")); act = new RadioUiAction(4, QList<int>() << Qt::Key_Right << Qt::Key_Minus, tr(SIMU_STR_HLP_KEY_RGT "|" SIMU_STR_HLP_KEY_MIN), tr(SIMU_STR_HLP_ACT_MIN));
ui->leftbuttons->addArea(polygon, "9X/9xcursmin.png", act); addRadioWidget(ui->leftbuttons->addArea(polygon, "9X/9xcursmin.png", act));
polygon.clear(); polygon.clear();
polygon << QPoint(x, y) << polyArc(x, y, oR, 225, 315); polygon << QPoint(x, y) << polyArc(x, y, oR, 225, 315);
act = addRadioUiAction(5, QList<int>() << Qt::Key_Left << Qt::Key_Minus, tr("LEFT/-"), tr("[ - ]")); act = new RadioUiAction(5, QList<int>() << Qt::Key_Left << Qt::Key_Plus << Qt::Key_Equal, tr(SIMU_STR_HLP_KEY_LFT "|" SIMU_STR_HLP_KEY_PLS), tr(SIMU_STR_HLP_ACT_PLS));
ui->leftbuttons->addArea(polygon, "9X/9xcursplus.png", act); addRadioWidget(ui->leftbuttons->addArea(polygon, "9X/9xcursplus.png", act));
act = addRadioUiAction(0, QList<int>() << Qt::Key_Enter << Qt::Key_Return, tr("ENTER/MOUSE-MID"), tr("[ MENU ]")); act = new RadioUiAction(0, QList<int>() << Qt::Key_Enter << Qt::Key_Return, tr(SIMU_STR_HLP_KEY_ENTER), tr(SIMU_STR_HLP_ACT_MENU));
m_rotEncClickAction = act; addRadioWidget(ui->rightbuttons->addArea(QRect(16, 54, 60, 34), "9X/9xmenumenu.png", act));
ui->rightbuttons->addArea(25, 60, 71, 81, "9X/9xmenumenu.png", act);
act = addRadioUiAction(1, QList<int>() << Qt::Key_Delete << Qt::Key_Escape << Qt::Key_Backspace, tr("DEL/BKSP/ESC"), tr("[ EXIT ]")); if (!hasRotEnc) {
ui->rightbuttons->addArea(25, 117, 71, 139, "9X/9xmenuexit.png", act); m_mouseMidClickAction = act;
m_mouseMidClickAction->setText(act->getText() % "|" SIMU_STR_HLP_MOUSE_MID);
}
ui->leftbuttons->addArea(-1, 148, 39, 182, "9X/9xcursphoto.png", m_screenshotAction); act = new RadioUiAction(1, QList<int>() << Qt::Key_Delete << Qt::Key_Escape << Qt::Key_Backspace, tr(SIMU_STR_HLP_KEYS_EXIT), tr(SIMU_STR_HLP_ACT_EXIT));
addRadioWidget(ui->rightbuttons->addArea(QRect(16, 114, 60, 34), "9X/9xmenuexit.png", act));
m_keymapHelp.append(keymapHelp_t(tr("WHEEL/PAD SCRL"), tr("[ UP ]/[ DN ] or Rotary Sel."))); addRadioWidget(ui->leftbuttons->addArea(QRect(6, 149, 30, 30), "9X/9xcursphoto.png", m_screenshotAction));
if (hasRotEnc) {
addRadioAction(new RadioUiAction(-1, 0, tr(SIMU_STR_HLP_MOUSE_SCRL), tr(SIMU_STR_HLP_ROTENC " " SIMU_STR_HLP_ROTENC_LR)));
m_mouseMidClickAction = new RadioUiAction(14, Qt::Key_Insert, tr(SIMU_STR_HLP_KEY_INS "|" SIMU_STR_HLP_MOUSE_MID), tr(SIMU_STR_HLP_ACT_ROT_DN));
addRadioWidget(ui->leftbuttons->addArea(QRect(0, 0, 0, 0), "9X/9xcurs.png", m_mouseMidClickAction));
}
m_backlightColors << QColor(159,165,247); m_backlightColors << QColor(159,165,247);
m_backlightColors << QColor(166,247,159); m_backlightColors << QColor(166,247,159);

View file

@ -35,48 +35,47 @@ SimulatedUIWidgetX12::SimulatedUIWidgetX12(SimulatorInterface *simulator, QWidge
int x = 74, y = 190, oR = 63, iR = 40; int x = 74, y = 190, oR = 63, iR = 40;
polygon << polyArc(x, y, oR, 225, 315) << polyArc(x, y, iR, 225, 315); polygon << polyArc(x, y, oR, 225, 315) << polyArc(x, y, iR, 225, 315);
act = addRadioUiAction(0, QList<int>() << Qt::Key_PageUp, tr("PG-UP"), tr("[ PgUp ]")); act = new RadioUiAction(0, QList<int>() << Qt::Key_PageUp, tr(SIMU_STR_HLP_KEY_PGUP), tr(SIMU_STR_HLP_ACT_PGUP));
ui->leftbuttons->addArea(polygon, "Horus/left_btn1.png", act); addRadioWidget(ui->leftbuttons->addArea(polygon, "Horus/left_btn1.png", act));
polygon.clear(); polygon.clear();
polygon << polyArc(x, y, oR, 135, 225) << polyArc(x, y, iR, 135, 225); polygon << polyArc(x, y, oR, 135, 225) << polyArc(x, y, iR, 135, 225);
act = addRadioUiAction(1, QList<int>() << Qt::Key_PageDown, tr("PG-DN"), tr("[ PgDn ]")); act = new RadioUiAction(1, QList<int>() << Qt::Key_PageDown, tr(SIMU_STR_HLP_KEY_PGDN), tr(SIMU_STR_HLP_ACT_PGDN));
ui->leftbuttons->addArea(polygon, "Horus/left_btn2.png", act); addRadioWidget(ui->leftbuttons->addArea(polygon, "Horus/left_btn2.png", act));
polygon.clear(); polygon.clear();
polygon << polyArc(x, y, oR, -45, 45) << polyArc(x, y, iR, -45, 45); polygon << polyArc(x, y, oR, -45, 45) << polyArc(x, y, iR, -45, 45);
act = addRadioUiAction(3, QList<int>() << Qt::Key_Up, tr("UP"), tr("[ MDL ]")); act = new RadioUiAction(3, QList<int>() << Qt::Key_Up, tr(SIMU_STR_HLP_KEY_UP), tr(SIMU_STR_HLP_ACT_MDL));
ui->rightbuttons->addArea(polygon, "Horus/right_btnU.png", act); addRadioWidget(ui->rightbuttons->addArea(polygon, "Horus/right_btnU.png", act));
polygon.clear();
polygon << polyArc(x, y, oR, 135, 225) << polyArc(x, y, iR, 135, 225);
act = addRadioUiAction(4, QList<int>() << Qt::Key_Down << Qt::Key_Delete << Qt::Key_Escape << Qt::Key_Backspace, tr("DN/DEL/BKSP"), tr("[ RTN ]"));
ui->rightbuttons->addArea(polygon, "Horus/right_btnD.png", act);
polygon.clear(); polygon.clear();
polygon << polyArc(x, y, oR, 225, 315) << polyArc(x, y, iR, 225, 315); polygon << polyArc(x, y, oR, 225, 315) << polyArc(x, y, iR, 225, 315);
act = addRadioUiAction(6, QList<int>() << Qt::Key_Left, tr("LEFT"), tr("[ SYS ]")); act = new RadioUiAction(6, QList<int>() << Qt::Key_Left, tr(SIMU_STR_HLP_KEY_LFT), tr(SIMU_STR_HLP_ACT_SYS));
ui->rightbuttons->addArea(polygon, "Horus/right_btnL.png", act); addRadioWidget(ui->rightbuttons->addArea(polygon, "Horus/right_btnL.png", act));
polygon.clear(); polygon.clear();
polygon << polyArc(x, y, oR, 45, 135) << polyArc(x, y, iR, 45, 135); polygon << polyArc(x, y, oR, 45, 135) << polyArc(x, y, iR, 45, 135);
act = addRadioUiAction(5, QList<int>() << Qt::Key_Right, tr("RIGHT"), tr("[ TELE ]")); act = new RadioUiAction(5, QList<int>() << Qt::Key_Right, tr(SIMU_STR_HLP_KEY_RGT), tr(SIMU_STR_HLP_ACT_TELE));
ui->rightbuttons->addArea(polygon, "Horus/right_btnR.png", act); addRadioWidget(ui->rightbuttons->addArea(polygon, "Horus/right_btnR.png", act));
m_keymapHelp.append(keymapHelp_t(tr("WHEEL/PAD SCRL"), tr("Rotary Selector"))); polygon.clear();
polygon << polyArc(x, y, oR, 135, 225) << polyArc(x, y, iR, 135, 225);
act = new RadioUiAction(4, QList<int>() << Qt::Key_Down << Qt::Key_Delete << Qt::Key_Escape << Qt::Key_Backspace,
tr(SIMU_STR_HLP_KEY_DN "<br>" SIMU_STR_HLP_KEYS_EXIT), tr(SIMU_STR_HLP_ACT_RTN));
addRadioWidget(ui->rightbuttons->addArea(polygon, "Horus/right_btnD.png", act));
m_scrollUpAction = addRadioUiAction(-1, QList<int>() << Qt::Key_Minus << Qt::Key_X, tr("-/X"), tr("Rotary UP")); m_scrollUpAction = new RadioUiAction(-1, QList<int>() << Qt::Key_Minus, tr(SIMU_STR_HLP_KEY_MIN "|" SIMU_STR_HLP_MOUSE_UP), tr(SIMU_STR_HLP_ACT_ROT_LFT));
m_scrollDnAction = addRadioUiAction(-1, QList<int>() << Qt::Key_Plus << Qt::Key_Equal << Qt::Key_C, tr("+/C"), tr("Rotary DOWN")); m_scrollDnAction = new RadioUiAction(-1, QList<int>() << Qt::Key_Plus << Qt::Key_Equal, tr(SIMU_STR_HLP_KEY_PLS "|" SIMU_STR_HLP_MOUSE_DN), tr(SIMU_STR_HLP_ACT_ROT_RGT));
connectScrollActions();
act = addRadioUiAction(2, QList<int>() << Qt::Key_Enter << Qt::Key_Return, tr("ENTER/MOUSE-MID"), tr("Selector Press")); m_mouseMidClickAction = new RadioUiAction(2, QList<int>() << Qt::Key_Enter << Qt::Key_Return, tr(SIMU_STR_HLP_KEYS_ACTIVATE), tr(SIMU_STR_HLP_ACT_ROT_DN));
ui->rightbuttons->addArea(polyArc(x, y, iR), "Horus/right_ent.png", act); addRadioWidget(ui->rightbuttons->addArea(polyArc(x, y, iR), "Horus/right_ent.png", m_mouseMidClickAction));
ui->leftbuttons->addArea(9, 259, 34, 282, "Horus/left_scrnsht.png", m_screenshotAction); addRadioWidget(ui->leftbuttons->addArea(QRect(9, 259, 30, 30), "Horus/left_scrnsht.png", m_screenshotAction));
m_backlightColors << QColor(47, 123, 227); m_backlightColors << QColor(47, 123, 227);
setLcd(ui->lcd); setLcd(ui->lcd);
connectScrollActions();
} }
SimulatedUIWidgetX12::~SimulatedUIWidgetX12() SimulatedUIWidgetX12::~SimulatedUIWidgetX12()

View file

@ -14,26 +14,25 @@ SimulatedUIWidgetX7::SimulatedUIWidgetX7(SimulatorInterface *simulator, QWidget
QPoint ctr(70, 91); QPoint ctr(70, 91);
polygon << polyArc(ctr.x(), ctr.y(), 50, -90, 90) << polyArc(ctr.x(), ctr.y(), 22, -90, 90); polygon << polyArc(ctr.x(), ctr.y(), 50, -90, 90) << polyArc(ctr.x(), ctr.y(), 22, -90, 90);
act = addRadioUiAction(3, QList<int>() << Qt::Key_PageUp, tr("PG-UP"), tr("[ PAGE ]")); act = new RadioUiAction(3, QList<int>() << Qt::Key_PageUp << Qt::Key_Up, tr(SIMU_STR_HLP_KEYS_GO_UP), tr(SIMU_STR_HLP_ACT_PAGE));
ui->leftbuttons->addArea(polygon, "X7/left_page.png", act); addRadioWidget(ui->leftbuttons->addArea(polygon, "X7/left_page.png", act));
act = addRadioUiAction(0, QList<int>() << Qt::Key_PageDown, tr("PG-DN"), tr("[ MENU ]")); act = new RadioUiAction(0, QList<int>() << Qt::Key_PageDown << Qt::Key_Down, tr(SIMU_STR_HLP_KEYS_GO_DN), tr(SIMU_STR_HLP_ACT_MENU_ICN));
ui->leftbuttons->addArea(polyArc(ctr.x(), ctr.y(), 20), "X7/left_menu.png", act); addRadioWidget(ui->leftbuttons->addArea(polyArc(ctr.x(), ctr.y(), 20), "X7/left_menu.png", act));
polygon.clear(); polygon.clear();
polygon << polyArc(ctr.x(), ctr.y(), 50, 90, 270) << polyArc(ctr.x(), ctr.y(), 22, 90, 270); polygon << polyArc(ctr.x(), ctr.y(), 50, 90, 270) << polyArc(ctr.x(), ctr.y(), 22, 90, 270);
act = addRadioUiAction(1, QList<int>() << Qt::Key_Delete << Qt::Key_Escape << Qt::Key_Backspace, tr("DEL/BKSP/ESC"), tr("[ EXIT ]")); act = new RadioUiAction(1, QList<int>() << Qt::Key_Delete << Qt::Key_Escape << Qt::Key_Backspace, tr(SIMU_STR_HLP_KEYS_EXIT), tr(SIMU_STR_HLP_ACT_EXIT));
ui->leftbuttons->addArea(polygon, "X7/left_exit.png", act); addRadioWidget(ui->leftbuttons->addArea(polygon, "X7/left_exit.png", act));
m_keymapHelp.append(keymapHelp_t(tr("WHEEL/PAD SCRL"), tr("Rotary Selector"))); m_scrollUpAction = new RadioUiAction(-1, QList<int>() << Qt::Key_Minus << Qt::Key_Equal << Qt::Key_Left, tr(SIMU_STR_HLP_KEYS_GO_LFT), tr(SIMU_STR_HLP_ACT_ROT_LFT));
m_scrollUpAction = addRadioUiAction(-1, QList<int>() << Qt::Key_Minus << Qt::Key_Equal << Qt::Key_Up, tr("-/UP"), tr("Rotary UP")); m_scrollDnAction = new RadioUiAction(-1, QList<int>() << Qt::Key_Plus << Qt::Key_Right, tr(SIMU_STR_HLP_KEYS_GO_RGT), tr(SIMU_STR_HLP_ACT_ROT_RGT));
m_scrollDnAction = addRadioUiAction(-1, QList<int>() << Qt::Key_Plus << Qt::Key_Down, tr("+/DN"), tr("Rotary DOWN"));
connectScrollActions(); connectScrollActions();
act = addRadioUiAction(2, QList<int>() << Qt::Key_Enter << Qt::Key_Return, tr("ENTER/MOUSE-MID"), tr("Selector Press")); m_mouseMidClickAction = new RadioUiAction(2, QList<int>() << Qt::Key_Enter << Qt::Key_Return, tr(SIMU_STR_HLP_KEYS_ACTIVATE), tr(SIMU_STR_HLP_ACT_ROT_DN));
ui->rightbuttons->addArea(polyArc(88, 92, 33), "X7/right_ent.png", act); addRadioWidget(ui->rightbuttons->addArea(polyArc(88, 92, 33), "X7/right_ent.png", m_mouseMidClickAction));
ui->leftbuttons->addArea(9, 154, 34, 177, "X7/left_scrnshot.png", m_screenshotAction); addRadioWidget(ui->leftbuttons->addArea(QRect(9, 154, 30, 30), "X7/left_scrnshot.png", m_screenshotAction));
m_backlightColors << QColor(215, 243, 255); // X7 Blue m_backlightColors << QColor(215, 243, 255); // X7 Blue
m_backlightColors << QColor(166,247,159); m_backlightColors << QColor(166,247,159);

View file

@ -29,7 +29,7 @@
</size> </size>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Taranis X7 Simulator</string> <string notr="true">Taranis X7 Simulator</string>
</property> </property>
<property name="styleSheet"> <property name="styleSheet">
<string notr="true">background-color: qlineargradient(spread:reflect, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 255), stop:0.757062 rgba(241, 238, 238, 255), stop:1 rgba(247, 245, 245, 255));</string> <string notr="true">background-color: qlineargradient(spread:reflect, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 255, 255, 255), stop:0.757062 rgba(241, 238, 238, 255), stop:1 rgba(247, 245, 245, 255));</string>

View file

@ -26,40 +26,46 @@ SimulatedUIWidgetX9::SimulatedUIWidgetX9(SimulatorInterface *simulator, QWidget
ui(new Ui::SimulatedUIWidgetX9) ui(new Ui::SimulatedUIWidgetX9)
{ {
RadioUiAction * act; RadioUiAction * act;
QPolygon polygon;
ui->setupUi(this); ui->setupUi(this);
// add actions in order of appearance on the help menu // add actions in order of appearance on the help menu
act = addRadioUiAction(0, QList<int>() << Qt::Key_PageUp, tr("PG-UP"), tr("[ MENU ]")); // button placement, size, spacing
polygon.setPoints(6, 20, 59, 27, 50, 45, 52, 56, 59, 50, 71, 26, 72); const int btop = 45, vsp = 17;
ui->leftbuttons->addArea(polygon, "Taranis/x9l1.png", act); QRect btn(16, btop, 46, 31);
act = addRadioUiAction(3, QList<int>() << Qt::Key_PageDown, tr("PG-DN"), tr("[ PAGE ]")); // left side
polygon.setPoints(6, 23, 107, 30, 99, 46, 100, 55, 106, 47, 117, 28, 117);
ui->leftbuttons->addArea(polygon, "Taranis/x9l2.png", act);
act = addRadioUiAction(1, QList<int>() << Qt::Key_Delete << Qt::Key_Escape << Qt::Key_Backspace, tr("DEL/BKSP/ESC"), tr("[ EXIT ]")); act = new RadioUiAction(0, QList<int>() << Qt::Key_Up << Qt::Key_PageUp, tr(SIMU_STR_HLP_KEYS_GO_UP), tr(SIMU_STR_HLP_ACT_MENU));
polygon.setPoints(6, 24, 154, 32, 144, 46, 146, 57, 156, 46, 167, 29, 166); addRadioWidget(ui->leftbuttons->addArea(btn, "Taranis/x9l1.png", act));
ui->leftbuttons->addArea(polygon, "Taranis/x9l3.png", act); btn.moveTop(btn.top() + btn.height() + vsp);
m_scrollUpAction = addRadioUiAction(4, QList<int>() << Qt::Key_Plus << Qt::Key_Equal << Qt::Key_Up, tr("+/UP"), tr("[ + ]")); act = new RadioUiAction(3, QList<int>() << Qt::Key_Down << Qt::Key_PageDown, tr(SIMU_STR_HLP_KEYS_GO_DN), tr(SIMU_STR_HLP_ACT_PAGE));
m_scrollDnAction = addRadioUiAction(5, QList<int>() << Qt::Key_Minus << Qt::Key_Down, tr("-/DN"), tr("[ - ]")); addRadioWidget(ui->leftbuttons->addArea(btn, "Taranis/x9l2.png", act));
btn.moveTop(btn.top() + btn.height() + vsp);
polygon.setPoints(6, 64, 60, 71, 50, 90, 50, 100, 60, 90, 73, 72, 73); act = new RadioUiAction(1, QList<int>() << Qt::Key_Delete << Qt::Key_Escape << Qt::Key_Backspace, tr(SIMU_STR_HLP_KEYS_EXIT), tr(SIMU_STR_HLP_ACT_EXIT));
ui->rightbuttons->addArea(polygon, "Taranis/x9r1.png", m_scrollUpAction); addRadioWidget(ui->leftbuttons->addArea(btn, "Taranis/x9l3.png", act));
polygon.setPoints(6, 63, 109, 73, 100, 88, 100, 98, 109, 88, 119, 72, 119); m_scrollUpAction = new RadioUiAction(4, QList<int>() << Qt::Key_Plus << Qt::Key_Equal << Qt::Key_Left,
ui->rightbuttons->addArea(polygon, "Taranis/x9r2.png", m_scrollDnAction); tr(SIMU_STR_HLP_KEY_LFT "|" SIMU_STR_HLP_KEY_PLS "|" SIMU_STR_HLP_MOUSE_UP), tr(SIMU_STR_HLP_ACT_PLS));
m_scrollDnAction = new RadioUiAction(5, QList<int>() << Qt::Key_Minus << Qt::Key_Right,
tr(SIMU_STR_HLP_KEY_RGT "|" SIMU_STR_HLP_KEY_MIN "|" SIMU_STR_HLP_MOUSE_DN), tr(SIMU_STR_HLP_ACT_MIN));
act = addRadioUiAction(2, QList<int>() << Qt::Key_Enter << Qt::Key_Return, tr("ENTER/MOUSE-MID"), tr("[ ENT ]")); // right side
polygon.setPoints(6, 63, 155, 72, 146, 90, 146, 98, 155, 88, 166, 72, 166); btn.moveTopLeft(QPoint(58, btop));
ui->rightbuttons->addArea(polygon, "Taranis/x9r3.png", act);
ui->leftbuttons->addArea(90, 177, 118, 197, "Taranis/x9l4.png", m_screenshotAction); addRadioWidget(ui->rightbuttons->addArea(btn, "Taranis/x9r1.png", m_scrollUpAction));
btn.moveTop(btn.top() + btn.height() + vsp);
m_keymapHelp.append(keymapHelp_t(tr("WHEEL/PAD SCRL"), tr("[ + ]/[ - ]"))); addRadioWidget(ui->rightbuttons->addArea(btn, "Taranis/x9r2.png", m_scrollDnAction));
btn.moveTop(btn.top() + btn.height() + vsp);
m_mouseMidClickAction = new RadioUiAction(2, QList<int>() << Qt::Key_Enter << Qt::Key_Return, tr(SIMU_STR_HLP_KEYS_ACTIVATE), tr(SIMU_STR_HLP_ACT_ENT));
addRadioWidget(ui->rightbuttons->addArea(btn, "Taranis/x9r3.png", m_mouseMidClickAction));
addRadioWidget(ui->leftbuttons->addArea(QRect(89, 177, 30, 20), "Taranis/x9l4.png", m_screenshotAction));
m_backlightColors << QColor(47, 123, 227); // Taranis Blue m_backlightColors << QColor(47, 123, 227); // Taranis Blue
m_backlightColors << QColor(166,247,159); m_backlightColors << QColor(166,247,159);

View file

@ -35,28 +35,27 @@ SimulatedUIWidgetX9E::SimulatedUIWidgetX9E(SimulatorInterface *simulator, QWidge
QPoint ctr(70, 100); QPoint ctr(70, 100);
polygon << polyArc(ctr.x(), ctr.y(), 60, -90, 0) << ctr; polygon << polyArc(ctr.x(), ctr.y(), 60, -90, 0) << ctr;
act = addRadioUiAction(0, QList<int>() << Qt::Key_PageUp, tr("PG-UP"), tr("[ MENU ]")); act = new RadioUiAction(0, QList<int>() << Qt::Key_Up << Qt::Key_PageUp, tr(SIMU_STR_HLP_KEYS_GO_UP), tr(SIMU_STR_HLP_ACT_MENU));
ui->leftbuttons->addArea(polygon, "X9E/left_menu.png", act); addRadioWidget(ui->leftbuttons->addArea(polygon, "X9E/left_menu.png", act));
polygon.clear(); polygon.clear();
polygon << polyArc(ctr.x(), ctr.y(), 45, 0, 180); polygon << polyArc(ctr.x(), ctr.y(), 45, 0, 180);
act = addRadioUiAction(3, QList<int>() << Qt::Key_PageDown, tr("PG-DN"), tr("[ PAGE ]")); act = new RadioUiAction(3, QList<int>() << Qt::Key_Down << Qt::Key_PageDown, tr(SIMU_STR_HLP_KEYS_GO_DN), tr(SIMU_STR_HLP_ACT_PAGE));
ui->leftbuttons->addArea(polygon, "X9E/left_page.png", act); addRadioWidget(ui->leftbuttons->addArea(polygon, "X9E/left_page.png", act));
polygon.clear(); polygon.clear();
polygon << polyArc(ctr.x(), ctr.y(), 60, 180, 270) << ctr; polygon << polyArc(ctr.x(), ctr.y(), 60, 180, 270) << ctr;
act = addRadioUiAction(1, QList<int>() << Qt::Key_Delete << Qt::Key_Escape << Qt::Key_Backspace, tr("DEL/BKSP/ESC"), tr("[ EXIT ]")); act = new RadioUiAction(1, QList<int>() << Qt::Key_Delete << Qt::Key_Escape << Qt::Key_Backspace, tr(SIMU_STR_HLP_KEYS_EXIT), tr(SIMU_STR_HLP_ACT_EXIT));
ui->leftbuttons->addArea(polygon, "X9E/left_exit.png", act); addRadioWidget(ui->leftbuttons->addArea(polygon, "X9E/left_exit.png", act));
m_keymapHelp.append(keymapHelp_t(tr("WHEEL/PAD SCRL"), tr("Rotary Selector"))); m_scrollUpAction = addRadioAction(new RadioUiAction(-1, QList<int>() << Qt::Key_Minus << Qt::Key_Left, tr(SIMU_STR_HLP_KEYS_GO_LFT), tr(SIMU_STR_HLP_ACT_ROT_LFT)));
m_scrollUpAction = addRadioUiAction(-1, QList<int>() << Qt::Key_Minus << Qt::Key_Equal << Qt::Key_Up, tr("-/UP"), tr("Rotary UP")); m_scrollDnAction = addRadioAction(new RadioUiAction(-1, QList<int>() << Qt::Key_Plus << Qt::Key_Equal << Qt::Key_Right, tr(SIMU_STR_HLP_KEYS_GO_RGT), tr(SIMU_STR_HLP_ACT_ROT_RGT)));
m_scrollDnAction = addRadioUiAction(-1, QList<int>() << Qt::Key_Plus << Qt::Key_Down, tr("+/DN"), tr("Rotary DOWN"));
connectScrollActions(); connectScrollActions();
act = addRadioUiAction(2, QList<int>() << Qt::Key_Enter << Qt::Key_Return, tr("ENTER/MOUSE-MID"), tr("Selector Press")); m_mouseMidClickAction = new RadioUiAction(2, QList<int>() << Qt::Key_Enter << Qt::Key_Return, tr(SIMU_STR_HLP_KEYS_ACTIVATE), tr(SIMU_STR_HLP_ACT_ROT_DN));
ui->rightbuttons->addArea(polyArc(90, 100, 50), "X9E/right_enter.png", act); addRadioWidget(ui->rightbuttons->addArea(polyArc(90, 100, 50), "X9E/right_enter.png", m_mouseMidClickAction));
ui->leftbuttons->addArea(10, 170, 40, 200, "X9E/left_scrnshot.png", m_screenshotAction); addRadioWidget(ui->leftbuttons->addArea(QRect(10, 170, 30, 30), "X9E/left_scrnshot.png", m_screenshotAction));
m_backlightColors << QColor(47, 123, 227); // Taranis Blue m_backlightColors << QColor(47, 123, 227); // Taranis Blue
m_backlightColors << QColor(166,247,159); m_backlightColors << QColor(166,247,159);

View file

@ -0,0 +1,66 @@
#ifndef SIMULATOR_STRINGS_H
#define SIMULATOR_STRINGS_H
#include <QtGlobal>
#define SIMU_STR_HLP_KEY_ENTER QT_TRANSLATE_NOOP("SimulatedUIWidget", "ENTER")
#define SIMU_STR_HLP_KEY_PGUP QT_TRANSLATE_NOOP("SimulatedUIWidget", "PG-UP")
#define SIMU_STR_HLP_KEY_PGDN QT_TRANSLATE_NOOP("SimulatedUIWidget", "PG-DN")
#define SIMU_STR_HLP_KEY_DEL QT_TRANSLATE_NOOP("SimulatedUIWidget", "DEL")
#define SIMU_STR_HLP_KEY_BKSP QT_TRANSLATE_NOOP("SimulatedUIWidget", "BKSP")
#define SIMU_STR_HLP_KEY_ESC QT_TRANSLATE_NOOP("SimulatedUIWidget", "ESC")
#define SIMU_STR_HLP_KEY_INS QT_TRANSLATE_NOOP("SimulatedUIWidget", "INS")
#define SIMU_STR_HLP_KEY_PLS QT_TRANSLATE_NOOP("SimulatedUIWidget", "<font size=+3>+</font>")
#define SIMU_STR_HLP_KEY_MIN QT_TRANSLATE_NOOP("SimulatedUIWidget", "<font size=+3>-</font>")
#define SIMU_STR_HLP_KEY_LFT QT_TRANSLATE_NOOP("SimulatedUIWidget", "<font size=+3>&larr;</font>") // ←
#define SIMU_STR_HLP_KEY_RGT QT_TRANSLATE_NOOP("SimulatedUIWidget", "<font size=+3>&rarr;</font>") // →
#define SIMU_STR_HLP_KEY_UP QT_TRANSLATE_NOOP("SimulatedUIWidget", "<font size=+3>&uarr;</font>") // ↑
#define SIMU_STR_HLP_KEY_DN QT_TRANSLATE_NOOP("SimulatedUIWidget", "<font size=+3>&darr;</font>") // ↓
#define SIMU_STR_HLP_ROTENC QT_TRANSLATE_NOOP("SimulatedUIWidget", "<font size=+3>&#x2686;</font>") // ⚆
#define SIMU_STR_HLP_ROTENC_LFT QT_TRANSLATE_NOOP("SimulatedUIWidget", "<font size=+3>&#x21b6;</font>") // ↶
#define SIMU_STR_HLP_ROTENC_RGT QT_TRANSLATE_NOOP("SimulatedUIWidget", "<font size=+3>&#x21b7;</font>") // ↷
#define SIMU_STR_HLP_ROTENC_LR QT_TRANSLATE_NOOP("SimulatedUIWidget", "<font size=+3>&#x21c6;</font>") // ⇆
#define SIMU_STR_HLP_ROTENC_DN QT_TRANSLATE_NOOP("SimulatedUIWidget", "<font size=+3>&#x21d3;</font>") // ⇓
#define SIMU_STR_HLP_SCRL_UP QT_TRANSLATE_NOOP("SimulatedUIWidget", "<font size=+3>&#x21d1;</font>") // ⇑
#define SIMU_STR_HLP_SCRL_DN QT_TRANSLATE_NOOP("SimulatedUIWidget", "<font size=+3>&#x21d3;</font>") // ⇓
#define SIMU_STR_HLP_SCRL_UD QT_TRANSLATE_NOOP("SimulatedUIWidget", "<font size=+3>&#x21d5;</font>") // ⇕
#define SIMU_STR_HLP_MOUSE QT_TRANSLATE_NOOP("SimulatedUIWidget", "<img src='qrc:/images/simulator/icons/svg/mouse.svg' width=20 height=18 />")
#define SIMU_STR_HLP_CLICK QT_TRANSLATE_NOOP("SimulatedUIWidget", "<img src='qrc:/images/simulator/icons/svg/arrow_click.svg' width=18 height=18 />")
#define SIMU_STR_HLP_MOUSE_MID SIMU_STR_HLP_MOUSE SIMU_STR_HLP_CLICK
#define SIMU_STR_HLP_MOUSE_UP SIMU_STR_HLP_MOUSE SIMU_STR_HLP_SCRL_UP
#define SIMU_STR_HLP_MOUSE_DN SIMU_STR_HLP_MOUSE SIMU_STR_HLP_SCRL_DN
#define SIMU_STR_HLP_MOUSE_SCRL SIMU_STR_HLP_MOUSE SIMU_STR_HLP_SCRL_UD
#define SIMU_STR_HLP_ACT_MENU QT_TRANSLATE_NOOP("SimulatedUIWidget", "<pre>[ MENU ]</pre>")
#define SIMU_STR_HLP_ACT_PAGE QT_TRANSLATE_NOOP("SimulatedUIWidget", "<pre>[ PAGE ]</pre>")
#define SIMU_STR_HLP_ACT_EXIT QT_TRANSLATE_NOOP("SimulatedUIWidget", "<pre>[ EXIT ]</pre>")
#define SIMU_STR_HLP_ACT_ENT QT_TRANSLATE_NOOP("SimulatedUIWidget", "<pre>[ ENT ]</pre>")
#define SIMU_STR_HLP_ACT_UP QT_TRANSLATE_NOOP("SimulatedUIWidget", "<pre>[ UP ]</pre>")
#define SIMU_STR_HLP_ACT_DN QT_TRANSLATE_NOOP("SimulatedUIWidget", "<pre>[ DN ]</pre>")
#define SIMU_STR_HLP_ACT_PLS QT_TRANSLATE_NOOP("SimulatedUIWidget", "<pre>[ <font size=+2>+</font> ]</pre>")
#define SIMU_STR_HLP_ACT_MIN QT_TRANSLATE_NOOP("SimulatedUIWidget", "<pre>[ <font size=+2>-</font> ]</pre>")
#define SIMU_STR_HLP_ACT_PGUP QT_TRANSLATE_NOOP("SimulatedUIWidget", "<pre>[ PgUp ]</pre>")
#define SIMU_STR_HLP_ACT_PGDN QT_TRANSLATE_NOOP("SimulatedUIWidget", "<pre>[ PgDn ]</pre>")
#define SIMU_STR_HLP_ACT_MDL QT_TRANSLATE_NOOP("SimulatedUIWidget", "<pre>[ MDL ]</pre>")
#define SIMU_STR_HLP_ACT_RTN QT_TRANSLATE_NOOP("SimulatedUIWidget", "<pre>[ RTN ]</pre>")
#define SIMU_STR_HLP_ACT_SYS QT_TRANSLATE_NOOP("SimulatedUIWidget", "<pre>[ SYS ]</pre>")
#define SIMU_STR_HLP_ACT_TELE QT_TRANSLATE_NOOP("SimulatedUIWidget", "<pre>[ TELE ]</pre>")
#define SIMU_STR_HLP_ACT_MENU_ICN QT_TRANSLATE_NOOP("SimulatedUIWidget", "<font size=+3>&#x2261;</font>") // ≡
#define SIMU_STR_HLP_KEYS_EXIT SIMU_STR_HLP_KEY_DEL "|" SIMU_STR_HLP_KEY_BKSP "|" SIMU_STR_HLP_KEY_ESC
#define SIMU_STR_HLP_KEYS_ACTIVATE SIMU_STR_HLP_KEY_ENTER "|" SIMU_STR_HLP_MOUSE_MID
#define SIMU_STR_HLP_KEYS_GO_UP SIMU_STR_HLP_KEY_UP "|" SIMU_STR_HLP_KEY_PGUP
#define SIMU_STR_HLP_KEYS_GO_DN SIMU_STR_HLP_KEY_DN "|" SIMU_STR_HLP_KEY_PGDN
#define SIMU_STR_HLP_KEYS_GO_LFT SIMU_STR_HLP_KEY_LFT "|" SIMU_STR_HLP_KEY_MIN "|" SIMU_STR_HLP_MOUSE_UP
#define SIMU_STR_HLP_KEYS_GO_RGT SIMU_STR_HLP_KEY_RGT "|" SIMU_STR_HLP_KEY_PLS "|" SIMU_STR_HLP_MOUSE_DN
#define SIMU_STR_HLP_ACT_ROT_LFT SIMU_STR_HLP_ROTENC " " SIMU_STR_HLP_ROTENC_LFT
#define SIMU_STR_HLP_ACT_ROT_RGT SIMU_STR_HLP_ROTENC " " SIMU_STR_HLP_ROTENC_RGT
#define SIMU_STR_HLP_ACT_ROT_DN SIMU_STR_HLP_ROTENC " " SIMU_STR_HLP_ROTENC_DN
#endif // SIMULATOR_STRINGS_H

View file

@ -1,114 +0,0 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#include <stdint.h>
#if !defined(MAX_LOGICAL_SWITCHES) && defined(NUM_CSW)
#define MAX_LOGICAL_SWITCHES NUM_CSW
#endif
#ifdef INIT_IMPORT
#undef INIT_IMPORT
#ifdef TELEMETRY_FRSKY
telemetryStreaming = 20;
#endif
#endif
#ifdef SETVALUES_IMPORT
#undef SETVALUES_IMPORT
for (int i=0; i<NUM_STICKS; i++)
g_anas[i] = inputs.sticks[i];
for (int i=0; i<NUM_POTS+NUM_SLIDERS; i++)
g_anas[NUM_STICKS+i] = inputs.pots[i];
for (int i=0; i<CPN_MAX_SWITCHES; i++)
simuSetSwitch(i, inputs.switches[i]);
for (int i=0; i<CPN_MAX_KEYS; i++)
simuSetKey(i, inputs.keys[i]);
for (int i=0; i<(NUM_STICKS+NUM_AUX_TRIMS)*2; i++)
simuSetTrim(i, inputs.trims[i]);
// rotary encoders
#if defined(PCBGRUVIN9X)
pind = 0;
if (inputs.rotenc)
pind |= 0x20;
#elif defined(PCBSKY9X)
if (inputs.rotenc)
PIOB->PIO_PDSR &= ~0x40;
else
PIOB->PIO_PDSR |= 0x40;
#else
if (inputs.rotenc)
simuSetKey(KEY_ENTER, true);
#endif
#endif // SETVALUES_IMPORT
#ifdef GETVALUES_IMPORT
#undef GETVALUES_IMPORT
memset(outputs.chans, 0, sizeof(outputs.chans));
for (unsigned int i=0; i<DIM(g_chans512); i++)
outputs.chans[i] = g_chans512[i];
for (int i=0; i<MAX_LOGICAL_SWITCHES; i++)
#if defined(BOLD_FONT)
outputs.vsw[i] = getSwitch(SWSRC_SW1+i);
#else
outputs.vsw[i] = getSwitch(SWSRC_SW1+i, 0);
#endif
#ifdef GVAR_VALUE // defined(GVARS)
/* TODO it could be a good idea instead of getPhase() / getPhaseName() outputs.phase = getFlightMode(); */
#if defined(GVARS)
for (int fm=0; fm<MAX_FLIGHT_MODES; fm++) {
for (int gv=0; gv<MAX_GVARS; gv++) {
outputs.gvars[fm][gv] = GVAR_VALUE(gv, getGVarFlightMode(fm, gv));
}
}
#endif
#endif
#endif
#ifdef LCDCHANGED_IMPORT
#undef LCDCHANGED_IMPORT
if (simuLcdRefresh) {
lightEnable = isBacklightEnabled();
simuLcdRefresh = false;
return true;
}
return false;
#endif
#ifdef TIMER10MS_IMPORT
#undef TIMER10MS_IMPORT
if (!main_thread_running)
return false;
per10ms();
return true;
#endif
#ifdef GETERROR_IMPORT
#undef GETERROR_IMPORT
return main_thread_error;
#endif
#ifdef SETTRAINER_IMPORT
#undef SETTRAINER_IMPORT
ppmInputValidityTimer = 100;
ppmInput[inputNumber] = qMin(qMax((int16_t)-512, value), (int16_t)512);
#endif

View file

@ -34,96 +34,141 @@
#include <QLibrary> #include <QLibrary>
#include <QMap> #include <QMap>
struct TxInputs #define SIMULATOR_INTERFACE_HEARTBEAT_PERIOD 1000 // ms
{
int sticks[CPN_MAX_STICKS]; /* lh lv rv rh */
int pots[CPN_MAX_POTS];
int switches[CPN_MAX_SWITCHES];
bool keys[CPN_MAX_KEYS];
bool rotenc;
bool trims[CPN_MAX_TRIM_SW];
};
class TxOutputs class SimulatorInterface : public QObject
{ {
Q_OBJECT
public: public:
TxOutputs() { memset(this, 0, sizeof(TxOutputs)); }
int chans[CPN_MAX_CHNOUT];
bool vsw[CPN_MAX_LOGICAL_SWITCHES];
int gvars[CPN_MAX_FLIGHT_MODES][CPN_MAX_GVARS];
unsigned int beep;
// uint8_t phase;
};
struct Trims enum InputSourceType {
{ INPUT_SRC_NONE = 0,
int values[CPN_MAX_TRIMS]; /* lh lv rv rh t5 t6 */ INPUT_SRC_ANALOG, // any analog source, index into g_anas[]
bool extended; INPUT_SRC_STICK, // Board::StickAxes, g_anas[index]
}; INPUT_SRC_KNOB, // g_anas[index + StickAxes]
INPUT_SRC_SLIDER, // g_anas[index + StickAxes + num_pots]
INPUT_SRC_TXVIN, // g_anas[Analogs::TX_VOLTAGE]
INPUT_SRC_SWITCH, // Named 2/3-pos switches
INPUT_SRC_TRIM_SW, // Board::TrimSwitches
INPUT_SRC_TRIM, // Board::TrimAxes
INPUT_SRC_KEY, // UI key/pushbutton
INPUT_SRC_ROTENC, // Rotary encoder (TODO)
INPUT_SRC_TRAINER, // Virtual trainer input
INPUT_SRC_ENUM_COUNT
};
class SimulatorInterface enum OutputSourceType {
{ OUTPUT_SRC_NONE = 0,
public: OUTPUT_SRC_CHAN_OUT,
OUTPUT_SRC_CHAN_MIX,
OUTPUT_SRC_TRIM_VALUE,
OUTPUT_SRC_TRIM_RANGE,
OUTPUT_SRC_VIRTUAL_SW,
OUTPUT_SRC_PHASE,
OUTPUT_SRC_GVAR,
OUTPUT_SRC_ENUM_COUNT
};
// only for data not available from Boards or Firmware, eg. compile-time options
enum Capability {
CAP_LUA, // LUA
CAP_ROTARY_ENC, // ROTARY_ENCODERS
CAP_ROTARY_ENC_NAV, // ROTARY_ENCODER_NAVIGATION
CAP_TELEM_FRSKY_SPORT, // TELEMETRY_FRSKY_SPORT
CAP_ENUM_COUNT
};
// This allows automatic en/decoding of flight mode + gvar value to/from any int32
struct gVarMode_t {
int16_t value;
uint8_t mode;
gVarMode_t(int i = 0) {
set(i);
}
void set(int i) {
mode = (i >> 16) & 0xFF;
value = (i & 0xFFFF);
}
operator int() {
return ((mode << 16) | (value & 0xFFFF));
}
gVarMode_t & operator =(const int i) {
set(i);
return *this;
}
};
struct TxOutputs {
TxOutputs() { clear(); }
void clear() { memset(this, 0, sizeof(TxOutputs)); }
int16_t chans[CPN_MAX_CHNOUT]; // final channel outputs
int16_t ex_chans[CPN_MAX_CHNOUT]; // raw mix outputs
int16_t gvars[CPN_MAX_FLIGHT_MODES][CPN_MAX_GVARS];
int trims[CPN_MAX_TRIMS]; // Board::TrimAxes enum
bool vsw[CPN_MAX_LOGICAL_SWITCHES]; // virtual/logic switches
int8_t phase;
qint16 trimRange; // TRIM_MAX or TRIM_EXTENDED_MAX
// bool beep;
};
virtual ~SimulatorInterface() {} virtual ~SimulatorInterface() {}
virtual void setSdPath(const QString & sdPath = "", const QString & settingsPath = "") { } virtual QString name() = 0;
virtual bool isRunning() = 0;
virtual void setVolumeGain(int value) { } virtual void readRadioData(QByteArray & dest) = 0;
virtual void start(QByteArray & eeprom, bool tests=true) = 0;
virtual void start(const char * filename, bool tests=true) = 0;
virtual void stop() = 0;
virtual void readEepromData(QByteArray & dest) = 0;
virtual bool timer10ms() = 0;
virtual uint8_t * getLcd() = 0; virtual uint8_t * getLcd() = 0;
virtual bool lcdChanged(bool &lightEnable) = 0;
virtual void setValues(TxInputs &inputs) = 0;
virtual void getValues(TxOutputs &outputs) = 0;
virtual void setTrim(unsigned int idx, int value) = 0;
virtual void getTrims(Trims &trims) = 0;
virtual unsigned int getPhase() = 0;
virtual const char * getPhaseName(unsigned int phase) = 0;
virtual void wheelEvent(int steps) { }
virtual const char * getError() = 0;
virtual void sendTelemetry(uint8_t * data, unsigned int len) = 0;
virtual uint8_t getSensorInstance(uint16_t id, uint8_t defaultValue = 0) = 0; virtual uint8_t getSensorInstance(uint16_t id, uint8_t defaultValue = 0) = 0;
virtual uint16_t getSensorRatio(uint16_t id) = 0; virtual uint16_t getSensorRatio(uint16_t id) = 0;
virtual const int getCapability(Capability cap) = 0;
public slots:
virtual void init() = 0;
virtual void start(const char * filename = NULL, bool tests = true) = 0;
virtual void stop() = 0;
virtual void setSdPath(const QString & sdPath = "", const QString & settingsPath = "") = 0;
virtual void setVolumeGain(const int value) = 0;
virtual void setRadioData(const QByteArray & data) = 0;
virtual void setAnalogValue(uint8_t index, int16_t value) = 0;
virtual void setKey(uint8_t key, bool state) = 0;
virtual void setSwitch(uint8_t swtch, int8_t state) = 0;
virtual void setTrim(unsigned int idx, int value) = 0;
virtual void setTrimSwitch(uint8_t trim, bool state) = 0;
virtual void setTrainerInput(unsigned int inputNumber, int16_t value) = 0; virtual void setTrainerInput(unsigned int inputNumber, int16_t value) = 0;
virtual void setInputValue(int type, uint8_t index, int16_t value) = 0;
virtual void installTraceHook(void (*callback)(const char *)) = 0; virtual void rotaryEncoderEvent(int steps) = 0;
virtual void setTrainerTimeout(uint16_t ms) = 0;
virtual void sendTelemetry(uint8_t * data, unsigned int len) = 0;
virtual void setLuaStateReloadPermanentScripts() = 0; virtual void setLuaStateReloadPermanentScripts() = 0;
virtual void addTracebackDevice(QIODevice * device) = 0;
virtual void removeTracebackDevice(QIODevice * device) = 0;
signals:
void started();
void stopped();
void heartbeat(qint32 loops, qint64 timestamp);
void runtimeError(const QString & error);
void lcdChange(bool backlightEnable);
void phaseChanged(qint8 phase, const QString & name);
void channelOutValueChange(quint8 index, qint32 value);
void channelMixValueChange(quint8 index, qint32 value);
void virtualSwValueChange(quint8 index, qint32 value);
void trimValueChange(quint8 index, qint32 value);
void trimRangeChange(quint8 index, qint32 min, qint16 max);
void gVarValueChange(quint8 index, qint32 value);
void outputValueChange(int type, quint8 index, qint32 value);
}; };
class SimulatorFactory { class SimulatorFactory {
public: public:
virtual ~SimulatorFactory() { } virtual ~SimulatorFactory() { }
virtual QString name() = 0; virtual QString name() = 0;
virtual Board::Type type() = 0; virtual Board::Type type() = 0;
virtual SimulatorInterface *create() = 0; virtual SimulatorInterface *create() = 0;
}; };

View file

@ -69,6 +69,9 @@ SimulatorMainWindow::SimulatorMainWindow(QWidget *parent, const QString & firmwa
return; return;
} }
m_simulator->moveToThread(&simuThread);
simuThread.start();
ui->setupUi(this); ui->setupUi(this);
setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea); setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
@ -103,38 +106,42 @@ SimulatorMainWindow::SimulatorMainWindow(QWidget *parent, const QString & firmwa
ui->menuView->insertSeparator(ui->actionToggleMenuBar); ui->menuView->insertSeparator(ui->actionToggleMenuBar);
ui->menuView->insertAction(ui->actionToggleMenuBar, ui->toolBar->toggleViewAction()); ui->menuView->insertAction(ui->actionToggleMenuBar, ui->toolBar->toggleViewAction());
// Hide some actions based on board capabilities. // Hide some actions based on simulator capabilities.
Firmware * firmware = getCurrentFirmware(); if(!m_simulator->getCapability(SimulatorInterface::CAP_LUA))
if(!firmware->getCapability(Capability(LuaInputsPerScript)))
ui->actionReloadLua->setDisabled(true); ui->actionReloadLua->setDisabled(true);
if (!firmware->getCapability(Capability(SportTelemetry))) if(!m_simulator->getCapability(SimulatorInterface::CAP_TELEM_FRSKY_SPORT))
m_telemetryDockWidget->toggleViewAction()->setDisabled(true); m_telemetryDockWidget->toggleViewAction()->setDisabled(true);
#ifndef JOYSTICKS #ifndef JOYSTICKS
ui->actionJoystickSettings->setDisabled(true); ui->actionJoystickSettings->setDisabled(true);
#endif #endif
// Add radio-specific help text from simulator widget
foreach (keymapHelp_t item, *m_simulatorWidget->getKeymapHelp())
m_keymapHelp.append(item);
restoreUiState(); restoreUiState();
setStyleSheet(SimulatorStyle::styleSheet()); setStyleSheet(SimulatorStyle::styleSheet());
connect(ui->actionShowKeymap, &QAction::triggered, this, &SimulatorMainWindow::showHelp); connect(ui->actionShowKeymap, &QAction::triggered, this, &SimulatorMainWindow::showHelp);
connect(ui->actionJoystickSettings, &QAction::triggered, this, &SimulatorMainWindow::openJoystickDialog); connect(ui->actionJoystickSettings, &QAction::triggered, this, &SimulatorMainWindow::openJoystickDialog);
connect(ui->actionReloadLua, &QAction::triggered, this, &SimulatorMainWindow::luaReload);
connect(ui->actionToggleMenuBar, &QAction::toggled, this, &SimulatorMainWindow::showMenuBar); connect(ui->actionToggleMenuBar, &QAction::toggled, this, &SimulatorMainWindow::showMenuBar);
connect(ui->actionFixedRadioWidth, &QAction::toggled, this, &SimulatorMainWindow::showRadioFixedWidth); connect(ui->actionFixedRadioWidth, &QAction::toggled, this, &SimulatorMainWindow::showRadioFixedWidth);
connect(ui->actionFixedRadioHeight, &QAction::toggled, this, &SimulatorMainWindow::showRadioFixedHeight); connect(ui->actionFixedRadioHeight, &QAction::toggled, this, &SimulatorMainWindow::showRadioFixedHeight);
connect(ui->actionDockRadio, &QAction::toggled, this, &SimulatorMainWindow::showRadioDocked); connect(ui->actionDockRadio, &QAction::toggled, this, &SimulatorMainWindow::showRadioDocked);
connect(ui->actionReloadRadioData, &QAction::triggered, this, &SimulatorMainWindow::simulatorRestart);
connect(ui->actionReloadLua, &QAction::triggered, m_simulator, &SimulatorInterface::setLuaStateReloadPermanentScripts);
if (m_outputsWidget) {
connect(this, &SimulatorMainWindow::simulatorStart, m_outputsWidget, &RadioOutputsWidget::start);
connect(this, &SimulatorMainWindow::simulatorRestart, m_outputsWidget, &RadioOutputsWidget::restart);
}
if (m_simulatorWidget) { if (m_simulatorWidget) {
connect(this, &SimulatorMainWindow::simulatorStart, m_simulatorWidget, &SimulatorWidget::start);
connect(this, &SimulatorMainWindow::simulatorRestart, m_simulatorWidget, &SimulatorWidget::restart);
connect(ui->actionScreenshot, &QAction::triggered, m_simulatorWidget, &SimulatorWidget::captureScreenshot); connect(ui->actionScreenshot, &QAction::triggered, m_simulatorWidget, &SimulatorWidget::captureScreenshot);
connect(ui->actionReloadRadioData, &QAction::triggered, m_simulatorWidget, &SimulatorWidget::restart);
connect(m_simulatorWidget, &SimulatorWidget::windowTitleChanged, this, &SimulatorMainWindow::setWindowTitle); connect(m_simulatorWidget, &SimulatorWidget::windowTitleChanged, this, &SimulatorMainWindow::setWindowTitle);
} }
if (m_outputsWidget)
connect(ui->actionReloadRadioData, &QAction::triggered, m_outputsWidget, &RadioOutputsWidget::restart);
} }
SimulatorMainWindow::~SimulatorMainWindow() SimulatorMainWindow::~SimulatorMainWindow()
@ -154,9 +161,11 @@ SimulatorMainWindow::~SimulatorMainWindow()
delete ui; delete ui;
if (m_simulator) if (m_simulator) {
simuThread.quit();
simuThread.wait();
delete m_simulator; delete m_simulator;
}
SimulatorLoader::unloadSimulator(m_simulatorId); SimulatorLoader::unloadSimulator(m_simulatorId);
} }
@ -256,11 +265,6 @@ bool SimulatorMainWindow::setOptions(SimulatorOptions & options, bool withSave)
void SimulatorMainWindow::start() void SimulatorMainWindow::start()
{ {
if (m_simulatorWidget)
m_simulatorWidget->start();
if (m_outputsWidget)
m_outputsWidget->start();
emit simulatorStart(); emit simulatorStart();
} }
@ -282,8 +286,6 @@ void SimulatorMainWindow::createDockWidgets()
m_telemetryDockWidget->setWidget(telem); m_telemetryDockWidget->setWidget(telem);
m_telemetryDockWidget->setObjectName("TELEMETRY_SIMULATOR"); m_telemetryDockWidget->setObjectName("TELEMETRY_SIMULATOR");
addTool(m_telemetryDockWidget, Qt::LeftDockWidgetArea, icon, QKeySequence(tr("F4"))); addTool(m_telemetryDockWidget, Qt::LeftDockWidgetArea, icon, QKeySequence(tr("F4")));
connect(this, &SimulatorMainWindow::simulatorStart, telem, &TelemetrySimulator::onSimulatorStarted);
connect(ui->actionReloadRadioData, &QAction::triggered, telem, &TelemetrySimulator::onSimulatorStarted);
} }
if (!m_trainerDockWidget) { if (!m_trainerDockWidget) {
@ -453,13 +455,6 @@ void SimulatorMainWindow::toggleRadioDocked(bool dock)
} }
void SimulatorMainWindow::luaReload(bool)
{
// force a reload of the lua environment
if (m_simulator)
m_simulator->setLuaStateReloadPermanentScripts();
}
void SimulatorMainWindow::openJoystickDialog(bool) void SimulatorMainWindow::openJoystickDialog(bool)
{ {
#ifdef JOYSTICKS #ifdef JOYSTICKS
@ -472,19 +467,34 @@ void SimulatorMainWindow::openJoystickDialog(bool)
void SimulatorMainWindow::showHelp(bool show) void SimulatorMainWindow::showHelp(bool show)
{ {
QString helpText = tr("Simulator Controls:"); QString helpText = ""
"<style>"
" td { text-align: center; vertical-align: middle; font-size: large; padding: 0 1em; white-space: nowrap; }"
" th { background-color: palette(alternate-base); }"
" img { vertical-align: text-top; }"
"</style>";
helpText += tr("<b>Simulator Controls:</b>");
helpText += "<table cellspacing=4 cellpadding=0>"; helpText += "<table cellspacing=4 cellpadding=0>";
helpText += tr("<tr><th>Key/Mouse</td><th>Action</td></tr>"); helpText += tr("<tr><th>Key/Mouse</th><th>Action</th></tr>", "note: must match html layout of each table row (keyTemplate).");
QString keyTemplate = "<tr><td align='center'><pre>%1</pre></td><td align='center'>%2</td></tr>";
foreach (keymapHelp_t pair, m_keymapHelp) QString keyTemplate = tr("<tr><td><kbd>%1</kbd></td><td>%2</td></tr>", "note: must match html layout of help text table header.");
keymapHelp_t pair;
// Add our own help text (if any)
foreach (pair, m_keymapHelp)
helpText += keyTemplate.arg(pair.first, pair.second); helpText += keyTemplate.arg(pair.first, pair.second);
// Add any radio-specific help text from simulator widget
foreach (pair, m_simulatorWidget->getKeymapHelp())
helpText += keyTemplate.arg(pair.first, pair.second);
helpText += "</table>"; helpText += "</table>";
QMessageBox * msgBox = new QMessageBox(this); QMessageBox * msgBox = new QMessageBox(this);
msgBox->setObjectName("SimulatorHelpText");
msgBox->setAttribute(Qt::WA_DeleteOnClose); msgBox->setAttribute(Qt::WA_DeleteOnClose);
msgBox->setWindowFlags(msgBox->windowFlags() | Qt::WindowStaysOnTopHint); msgBox->setWindowFlags(msgBox->windowFlags() | Qt::WindowStaysOnTopHint);
msgBox->setStandardButtons( QMessageBox::Ok ); msgBox->setStandardButtons( QMessageBox::Ok );
msgBox->setWindowTitle(tr("Simulator Help")); msgBox->setWindowTitle(tr("Simulator Help"));
msgBox->setTextFormat(Qt::RichText);
msgBox->setText(helpText); msgBox->setText(helpText);
msgBox->setModal(false); msgBox->setModal(false);
msgBox->show(); msgBox->show();

View file

@ -25,6 +25,7 @@
#include <QDockWidget> #include <QDockWidget>
#include <QMainWindow> #include <QMainWindow>
#include <QThread>
class DebugOutput; class DebugOutput;
class RadioData; class RadioData;
@ -67,6 +68,7 @@ class SimulatorMainWindow : public QMainWindow
signals: signals:
void simulatorStart(); void simulatorStart();
void simulatorRestart();
protected slots: protected slots:
virtual void closeEvent(QCloseEvent *); virtual void closeEvent(QCloseEvent *);
@ -76,7 +78,6 @@ class SimulatorMainWindow : public QMainWindow
void toggleMenuBar(bool show); void toggleMenuBar(bool show);
void setRadioSizePolicy(int fixType); void setRadioSizePolicy(int fixType);
void toggleRadioDocked(bool dock); void toggleRadioDocked(bool dock);
void luaReload(bool);
void openJoystickDialog(bool); void openJoystickDialog(bool);
void showHelp(bool show); void showHelp(bool show);
@ -97,6 +98,7 @@ class SimulatorMainWindow : public QMainWindow
QDockWidget * m_trainerDockWidget; QDockWidget * m_trainerDockWidget;
QDockWidget * m_outputsDockWidget; QDockWidget * m_outputsDockWidget;
QThread simuThread;
QVector<keymapHelp_t> m_keymapHelp; QVector<keymapHelp_t> m_keymapHelp;
QString m_simulatorId; QString m_simulatorId;
QString m_exitStatusMsg; QString m_exitStatusMsg;

View file

@ -24,12 +24,13 @@
#include "appdata.h" #include "appdata.h"
#include "appdebugmessagehandler.h" #include "appdebugmessagehandler.h"
#include "radiofaderwidget.h" #include "radiofaderwidget.h"
#include "radiokeywidget.h"
#include "radioknobwidget.h" #include "radioknobwidget.h"
#include "radioswitchwidget.h" #include "radioswitchwidget.h"
#include "radiotrimwidget.h"
#include "radiouiaction.h" #include "radiouiaction.h"
#include "sdcard.h" #include "sdcard.h"
#include "simulateduiwidget.h" #include "simulateduiwidget.h"
#include "simulatorinterface.h"
#include "storage.h" #include "storage.h"
#include "virtualjoystickwidget.h" #include "virtualjoystickwidget.h"
#ifdef JOYSTICKS #ifdef JOYSTICKS
@ -41,36 +42,27 @@
#include <QMessageBox> #include <QMessageBox>
#include <iostream> #include <iostream>
SimulatorWidget::SimulatorWidget(QWidget * parent, SimulatorInterface *simulator, quint8 flags): using namespace Simulator;
SimulatorWidget::SimulatorWidget(QWidget * parent, SimulatorInterface * simulator, quint8 flags):
QWidget(parent), QWidget(parent),
ui(new Ui::SimulatorWidget), ui(new Ui::SimulatorWidget),
simulator(simulator), simulator(simulator),
firmware(getCurrentFirmware()), firmware(getCurrentFirmware()),
radioSettings(GeneralSettings()), radioSettings(GeneralSettings()),
timer(new QTimer(this)),
radioUiWidget(NULL), radioUiWidget(NULL),
vJoyLeft(NULL), vJoyLeft(NULL),
vJoyRight(NULL), vJoyRight(NULL),
m_board(getCurrentBoard()), m_board(getCurrentBoard()),
flags(flags), flags(flags),
lastPhase(-1),
buttonPressed(0),
trimPressed(255),
startupFromFile(false), startupFromFile(false),
deleteTempRadioData(false), deleteTempRadioData(false),
saveTempRadioData(false), saveTempRadioData(false)
middleButtonPressed(false),
firstShow(true)
{
#ifdef JOYSTICKS #ifdef JOYSTICKS
joystick = NULL; , joystick(NULL)
#endif #endif
// defaults {
setRadioProfileId(g.sessionId());
setSdPath(g.profile[radioProfileId].sdPath());
ui->setupUi(this); ui->setupUi(this);
windowName = tr("Radio Simulator (%1)").arg(firmware->getName()); windowName = tr("Radio Simulator (%1)").arg(firmware->getName());
@ -96,7 +88,7 @@ SimulatorWidget::SimulatorWidget(QWidget * parent, SimulatorInterface *simulator
break; break;
} }
foreach (keymapHelp_t item, *radioUiWidget->getKeymapHelp()) foreach (keymapHelp_t item, radioUiWidget->getKeymapHelp())
keymapHelp.append(item); keymapHelp.append(item);
ui->radioUiWidget->layout()->removeItem(ui->radioUiTempSpacer); ui->radioUiWidget->layout()->removeItem(ui->radioUiTempSpacer);
@ -105,6 +97,7 @@ SimulatorWidget::SimulatorWidget(QWidget * parent, SimulatorInterface *simulator
radioUiWidget->setFocusPolicy(Qt::WheelFocus); radioUiWidget->setFocusPolicy(Qt::WheelFocus);
radioUiWidget->setFocus(); radioUiWidget->setFocus();
connect(radioUiWidget, &SimulatedUIWidget::controlValueChange, this, &SimulatorWidget::onRadioWidgetValueChange);
connect(radioUiWidget, &SimulatedUIWidget::customStyleRequest, this, &SimulatorWidget::setUiAreaStyle); connect(radioUiWidget, &SimulatedUIWidget::customStyleRequest, this, &SimulatorWidget::setUiAreaStyle);
vJoyLeft = new VirtualJoystickWidget(this, 'L'); vJoyLeft = new VirtualJoystickWidget(this, 'L');
@ -113,25 +106,43 @@ SimulatorWidget::SimulatorWidget(QWidget * parent, SimulatorInterface *simulator
vJoyRight = new VirtualJoystickWidget(this, 'R'); vJoyRight = new VirtualJoystickWidget(this, 'R');
ui->rightStickLayout->addWidget(vJoyRight); ui->rightStickLayout->addWidget(vJoyRight);
connect(vJoyLeft, &VirtualJoystickWidget::trimButtonPressed, this, &SimulatorWidget::onTrimPressed); connect(vJoyLeft, &VirtualJoystickWidget::valueChange, this, &SimulatorWidget::onRadioWidgetValueChange);
connect(vJoyLeft, &VirtualJoystickWidget::trimButtonReleased, this, &SimulatorWidget::onTrimReleased); connect(vJoyRight, &VirtualJoystickWidget::valueChange, this, &SimulatorWidget::onRadioWidgetValueChange);
connect(vJoyLeft, &VirtualJoystickWidget::trimSliderMoved, this, &SimulatorWidget::onTrimSliderMoved); 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(this, &SimulatorWidget::simulatorInit, simulator, &SimulatorInterface::init);
connect(vJoyRight, &VirtualJoystickWidget::trimButtonReleased, this, &SimulatorWidget::onTrimReleased); connect(this, &SimulatorWidget::simulatorStart, simulator, &SimulatorInterface::start);
connect(vJoyRight, &VirtualJoystickWidget::trimSliderMoved, this, &SimulatorWidget::onTrimSliderMoved); 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(simulator, &SimulatorInterface::started, this, &SimulatorWidget::onSimulatorStarted);
connect(timer, SIGNAL(timeout()), this, SLOT(onTimerEvent())); connect(simulator, &SimulatorInterface::stopped, this, &SimulatorWidget::onSimulatorStopped);
connect(timer, SIGNAL(timeout()), radioUiWidget, SLOT(updateUi())); 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() SimulatorWidget::~SimulatorWidget()
{ {
shutdown(); shutdown();
if (timer)
timer->deleteLater();
if (radioUiWidget) if (radioUiWidget)
delete radioUiWidget; delete radioUiWidget;
if (vJoyLeft) if (vJoyLeft)
@ -142,10 +153,7 @@ SimulatorWidget::~SimulatorWidget()
if (joystick) if (joystick)
delete joystick; delete joystick;
#endif #endif
firmware = NULL;
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.
delete ui; delete ui;
} }
@ -168,8 +176,7 @@ void SimulatorWidget::setPaths(const QString & sdPath, const QString & dataPath)
{ {
sdCardPath = sdPath; sdCardPath = sdPath;
radioDataPath = dataPath; radioDataPath = dataPath;
if (simulator) emit simulatorSdPathChange(sdPath, dataPath);
simulator->setSdPath(sdPath, dataPath);
} }
void SimulatorWidget::setRadioSettings(const GeneralSettings settings) void SimulatorWidget::setRadioSettings(const GeneralSettings settings)
@ -264,7 +271,7 @@ bool SimulatorWidget::setRadioData(RadioData * radioData)
if (ret) { if (ret) {
if (radioDataPath.isEmpty()) { 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) { if (firmware->getEEpromInterface()->save((uint8_t *)startupData.data(), *radioData, 0, firmware->getCapability(SimulatorVariant)) <= 0) {
ret = false; ret = false;
} }
@ -410,8 +417,7 @@ void SimulatorWidget::deleteTempData()
void SimulatorWidget::saveState() void SimulatorWidget::saveState()
{ {
SimulatorOptions opts = g.profile[radioProfileId].simulatorOptions(); SimulatorOptions opts = g.profile[radioProfileId].simulatorOptions();
//opts.windowGeometry = saveGeometry(); saveRadioWidgetsState(opts.controlsState);
opts.controlsState = saveRadioWidgetsState();
g.profile[radioProfileId].simulatorOptions(opts); g.profile[radioProfileId].simulatorOptions(opts);
} }
@ -432,35 +438,58 @@ void SimulatorWidget::captureScreenshot(bool)
void SimulatorWidget::start() void SimulatorWidget::start()
{ {
emit simulatorInit(); // init simulator default I/O values
setupRadioWidgets(); setupRadioWidgets();
setupJoysticks();
restoreRadioWidgetsState(); restoreRadioWidgetsState();
if (startupData.isEmpty()) bool tests = !(flags & SIMULATOR_FLAGS_NOTX);
simulator->start((const char *)0); if (!startupData.isEmpty()) {
else if (startupFromFile) if (startupFromFile) {
simulator->start(startupData.constData()); emit simulatorStart(startupData.constData(), tests);
else }
simulator->start(startupData, (flags & SIMULATOR_FLAGS_NOTX) ? false : true); else {
simulator->setRadioData(startupData);
setTrims(); emit simulatorStart((const char *)0, tests);
getValues(); }
setupTimer(); }
else {
emit simulatorStart((const char *)0, tests);
}
} }
void SimulatorWidget::stop() void SimulatorWidget::stop()
{ {
if (timer) emit simulatorStop();
timer->stop(); QElapsedTimer tmout;
if (simulator) { tmout.start();
simulator->stop(); // block until simulator stops or times out
if (saveTempRadioData) { while (simulator->isRunning()) {
startupData.fill(0, getEEpromSize(m_board)); if (tmout.hasExpired(2000)) {
simulator->readEepromData(startupData); 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() void SimulatorWidget::restart()
{ {
stop(); stop();
@ -486,100 +515,112 @@ void SimulatorWidget::shutdown()
void SimulatorWidget::setRadioProfileId(int value) void SimulatorWidget::setRadioProfileId(int value)
{ {
radioProfileId = value; radioProfileId = value;
if (simulator) emit simulatorVolumeGainChange(g.profile[radioProfileId].volumeGain());
simulator->setVolumeGain(g.profile[radioProfileId].volumeGain());
} }
void SimulatorWidget::setupRadioWidgets() void SimulatorWidget::setupRadioWidgets()
{ {
int i, midpos, aIdx;
QString wname; 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. // First clear out any existing widgets.
if (switches.size()) { foreach (RadioWidget * rw, m_radioWidgets) {
foreach (RadioWidget * w, switches) { switch(rw->getType()) {
ui->radioWidgetsHTLayout->removeWidget(w); case RadioWidget::RADIO_WIDGET_SWITCH :
w->deleteLater(); 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(); disconnect(rw, 0, this, 0);
} disconnect(this, 0, rw, 0);
if (analogs.size()) { rw->deleteLater();
foreach (RadioWidget * w, analogs) {
if (w->getType() == RadioWidget::RADIO_WIDGET_KNOB)
ui->radioWidgetsHTLayout->removeWidget(w);
else
ui->VCGridLayout->removeWidget(w);
w->deleteLater();
}
analogs.clear();
} }
m_radioWidgets.clear();
// Now set up new widgets. // Now set up new widgets.
// switches // switches
Board::SwitchInfo switchInfo;
Board::SwitchType swcfg; 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) if (radioSettings.switchConfig[i] == Board::SWITCH_NOT_AVAILABLE)
continue; continue;
swcfg = Board::SwitchType(radioSettings.switchConfig[i]); swcfg = Board::SwitchType(radioSettings.switchConfig[i]);
wname = RawSource(RawSourceType::SOURCE_TYPE_SWITCH, i).toString(NULL, &radioSettings);
if ((wname = QString(radioSettings.switchName[i])).isEmpty()) {
switchInfo = getSwitchInfo(board, i);
wname = QString(switchInfo.name);
}
RadioSwitchWidget * sw = new RadioSwitchWidget(swcfg, wname, -1, ui->radioWidgetsHT); RadioSwitchWidget * sw = new RadioSwitchWidget(swcfg, wname, -1, ui->radioWidgetsHT);
sw->setIndex(i); sw->setIndex(i);
ui->radioWidgetsHTLayout->addWidget(sw); ui->radioWidgetsHTLayout->addWidget(sw);
switches.append(sw);
m_radioWidgets.append(sw);
} }
midpos = (int)floorf(switches.size() / 2.0f); midpos = (int)floorf(m_radioWidgets.size() / 2.0f);
aIdx = 0;
// pots in middle of switches // 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)) if (!radioSettings.isPotAvailable(i))
continue; continue;
if ((wname = QString(radioSettings.potName[i])).isEmpty()) wname = RawSource(RawSourceType::SOURCE_TYPE_STICK, ttlSticks + i).toString(NULL, &radioSettings);
wname = firmware->getAnalogInputName(4 + aIdx + i);
RadioKnobWidget * pot = new RadioKnobWidget(Board::PotType(radioSettings.potConfig[i]), wname, 0, ui->radioWidgetsHT); RadioKnobWidget * pot = new RadioKnobWidget(Board::PotType(radioSettings.potConfig[i]), wname, 0, ui->radioWidgetsHT);
pot->setIndex(aIdx + i); pot->setIndex(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);
ui->radioWidgetsHTLayout->insertWidget(midpos++, pot); ui->radioWidgetsHTLayout->insertWidget(midpos++, pot);
analogs.append(pot);
m_radioWidgets.append(pot);
} }
aIdx += i;
// faders between sticks // faders between sticks
int r = 0, c = 0; int c = extraTrims / 2; // leave space for any extra trims
for (i = 0; i < getBoardCapability(board, Board::Sliders) && i + aIdx < CPN_MAX_POTS; ++i) { for (i = 0; i < ttlFaders; ++i) {
if (!radioSettings.isSliderAvailable(i)) if (!radioSettings.isSliderAvailable(i))
continue; continue;
if ((wname = QString(radioSettings.sliderName[i])).isEmpty()) wname = RawSource(RawSourceType::SOURCE_TYPE_STICK, ttlSticks + ttlKnobs + i).toString(NULL, &radioSettings);
wname = firmware->getAnalogInputName(4 + aIdx + i);
RadioFaderWidget * sl = new RadioFaderWidget(wname, 0, ui->radioWidgetsVC); RadioFaderWidget * sl = new RadioFaderWidget(wname, 0, ui->radioWidgetsVC);
sl->setIndex(aIdx + i); sl->setIndex(i);
// FIXME : total hack here -- this needs to follow the exception in radio/src/mixer.cpp:evalInputs() ui->VCGridLayout->addWidget(sl, 0, c++, 1, 1);
if (i == 0 && IS_TARANIS(board) && !IS_TARANIS_X7(board))
sl->setInvertValue(true); m_radioWidgets.append(sl);
/* 2-row option
if (!(i % 2)) {
++r;
c = 0;
} */
ui->VCGridLayout->addWidget(sl, r, c++, 1, 1);
analogs.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() void SimulatorWidget::setupJoysticks()
@ -600,6 +641,10 @@ void SimulatorWidget::setupJoysticks()
joystick->deadzones[j] = 0; joystick->deadzones[j] = 0;
} }
connect(joystick, &Joystick::axisValueChanged, this, &SimulatorWidget::onjoystickAxisValueChanged); 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; joysticksEnabled = true;
} }
else { else {
@ -609,164 +654,50 @@ void SimulatorWidget::setupJoysticks()
else if (joystick) { else if (joystick) {
joystick->close(); joystick->close();
disconnect(joystick, 0, this, 0); disconnect(joystick, 0, this, 0);
if (vJoyLeft)
disconnect(this, 0, vJoyLeft, 0);
if (vJoyRight)
disconnect(this, 0, vJoyRight, 0);
joystick->deleteLater(); joystick->deleteLater();
joystick = NULL; joystick = NULL;
} }
if (vJoyRight) { if (vJoyRight)
vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_X, joysticksEnabled); vJoyRight->setStickConstraint((VirtualJoystickWidget::HOLD_X | VirtualJoystickWidget::HOLD_Y), joysticksEnabled);
vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_Y, joysticksEnabled); if (vJoyLeft)
} vJoyLeft->setStickConstraint((VirtualJoystickWidget::HOLD_X | VirtualJoystickWidget::HOLD_Y), joysticksEnabled);
if (vJoyLeft) {
vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_X, joysticksEnabled);
vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_Y, joysticksEnabled);
}
#endif #endif
} }
void SimulatorWidget::setupTimer()
{
if (!timer)
return;
if (timer->isActive())
timer->stop();
timer->start();
}
void SimulatorWidget::restoreRadioWidgetsState() void SimulatorWidget::restoreRadioWidgetsState()
{ {
// All RadioWidgets
RadioWidget::RadioWidgetState state; RadioWidget::RadioWidgetState state;
QMap<int, QByteArray> switchesMap;
QMap<int, QByteArray> analogsMap;
QList<QByteArray> states = g.profile[radioProfileId].simulatorOptions().controlsState; QList<QByteArray> states = g.profile[radioProfileId].simulatorOptions().controlsState;
foreach (QByteArray ba, states) { foreach (QByteArray ba, states) {
QDataStream stream(ba); QDataStream stream(ba);
stream >> state; stream >> state;
if (state.type == RadioWidget::RADIO_WIDGET_SWITCH) emit widgetStateChange(state);
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()));
} }
// Set throttle stick down and locked, side depends on mode // Set throttle stick down and locked, side depends on mode
if (radioSettings.stickMode & 1) { emit stickModeChange(radioSettings.stickMode);
vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_Y, true);
vJoyLeft->setStickY(1); // TODO : custom voltages
} qint16 volts = radioSettings.vBatWarn + 2;
else { if (firmware->getCapability(Capability::HasBatMeterRange))
vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_Y, true); volts = (radioSettings.vBatMin + 90) + ((radioSettings.vBatMax + 30) - radioSettings.vBatMin) / 2;
vJoyRight->setStickY(1); emit inputValueChange(SimulatorInterface::INPUT_SRC_TXVIN, 0, volts);
}
} }
QList<QByteArray> SimulatorWidget::saveRadioWidgetsState() void SimulatorWidget::saveRadioWidgetsState(QList<QByteArray> & state)
{ {
QList<QByteArray> states; if (m_radioWidgets.size()) {
state.clear();
for (int i = 0; i < analogs.size(); ++i) foreach (RadioWidget * rw, m_radioWidgets)
states.append(analogs[i]->getStateData()); state.append(rw->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));
} }
} }
// "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 * Event handlers/private slots
@ -792,51 +723,95 @@ void SimulatorWidget::wheelEvent(QWheelEvent *event)
void SimulatorWidget::onTimerEvent() 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()) { void SimulatorWidget::onSimulatorHeartbeat(qint32 loops, qint64 timestamp)
QMessageBox::critical(this, "Companion", tr("Firmware %1 error: %2").arg(firmware->getName()).arg(simulator->getError())); {
timer->stop(); 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; return;
} }
getValues(); emit inputValueChange(inpType, index, value);
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();
} }
void SimulatorWidget::onjoystickAxisValueChanged(int axis, int value) void SimulatorWidget::onjoystickAxisValueChanged(int axis, int value)
{ {
#ifdef JOYSTICKS #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; static const int valueRange = 1024;
if (!joystick || axis >= MAX_JOYSTICKS) if (!joystick || axis >= MAX_JOYSTICKS)
@ -845,7 +820,7 @@ void SimulatorWidget::onjoystickAxisValueChanged(int axis, int value)
int dlta; int dlta;
int stick = g.joystick[axis].stick_axe(); int stick = g.joystick[axis].stick_axe();
if (stick < 0 || stick >= ttlSticks + analogs.count()) if (stick < 0 || stick >= ttlSticks + ttlKnobs + ttlFaders)
return; return;
int stickval = valueRange * (value - g.joystick[axis].stick_med()); 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()) if (g.joystick[axis].stick_inv())
stickval *= -1; stickval *= -1;
if (stick==1 ) { if (stick < ttlSticks) {
vJoyRight->setStickY(-stickval/1024.0); emit stickValueChange(stick, stickval);
} }
else if (stick==2) { else {
vJoyRight->setStickX(stickval/1024.0); stick -= ttlSticks;
} if (stick < ttlKnobs)
else if (stick==3) { emit widgetValueChange(RadioWidget::RADIO_WIDGET_KNOB, stick, stickval);
vJoyLeft->setStickY(-stickval/1024.0); else
} emit widgetValueChange(RadioWidget::RADIO_WIDGET_FADER, stick, stickval);
else if (stick==4) {
vJoyLeft->setStickX(stickval/1024.0);
}
else if (stick > ttlSticks) {
analogs[stick-ttlSticks-1]->setValue(stickval);
} }
#endif #endif

View file

@ -24,24 +24,20 @@
#include "constants.h" #include "constants.h"
#include "helpers.h" #include "helpers.h"
#include "radiodata.h" #include "radiodata.h"
#include "radiowidget.h"
#include "simulator.h" #include "simulator.h"
#include "simulatorinterface.h"
#include <QElapsedTimer>
#include <QTimer> #include <QTimer>
#include <QWidget> #include <QWidget>
#include <QVector> #include <QVector>
#define FLASH_DURATION 10
#define CBEEP_ON "QLabel { background-color: #FF364E }"
#define CBEEP_OFF "QLabel { }"
void traceCb(const char * text); void traceCb(const char * text);
class Firmware; class Firmware;
class SimulatorInterface; class SimulatorInterface;
class SimulatedUIWidget; class SimulatedUIWidget;
class RadioWidget;
class RadioSwitchWidget;
class VirtualJoystickWidget; class VirtualJoystickWidget;
#ifdef JOYSTICKS #ifdef JOYSTICKS
class Joystick; class Joystick;
@ -56,8 +52,6 @@ namespace Ui {
class SimulatorWidget; class SimulatorWidget;
} }
using namespace Simulator;
class SimulatorWidget : public QWidget class SimulatorWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
@ -84,7 +78,7 @@ class SimulatorWidget : public QWidget
QString getSdPath() const { return sdCardPath; } QString getSdPath() const { return sdCardPath; }
QString getDataPath() const { return radioDataPath; } QString getDataPath() const { return radioDataPath; }
QVector<keymapHelp_t> * getKeymapHelp() { return &keymapHelp; } QVector<Simulator::keymapHelp_t> getKeymapHelp() const { return keymapHelp; }
public slots: public slots:
void start(); void start();
@ -92,52 +86,18 @@ class SimulatorWidget : public QWidget
void restart(); void restart();
void shutdown(); void shutdown();
private: signals:
void setRadioProfileId(int value); void stickValueChange(int axis, int value);
void setupRadioWidgets(); void stickModeChange(const unsigned mode);
void setupTimer(); void widgetValueChange(const RadioWidget::RadioWidgetType type, const int index, const int value);
void restoreRadioWidgetsState(); void widgetStateChange(const RadioWidget::RadioWidgetState & state);
QList<QByteArray> saveRadioWidgetsState(); void inputValueChange(int type, quint8 index, qint16 value);
void simulatorSetData(const QByteArray & data);
void setValues(); void simulatorInit();
void getValues(); void simulatorStart(const char * filename, bool tests);
void setTrims(); void simulatorStop();
void simulatorSdPathChange(const QString & sdPath, const QString & dataPath);
void simulatorVolumeGainChange(const int gain);
Ui::SimulatorWidget * ui;
SimulatorInterface * simulator;
Firmware * firmware;
GeneralSettings radioSettings;
QTimer * timer;
QString windowName;
QVector<keymapHelp_t> keymapHelp;
SimulatedUIWidget * radioUiWidget;
VirtualJoystickWidget * vJoyLeft;
VirtualJoystickWidget * vJoyRight;
QVector<RadioSwitchWidget *> switches;
QVector<RadioWidget *> analogs;
QString sdCardPath;
QString radioDataPath;
QByteArray startupData;
Board::Type m_board;
quint8 flags;
int radioProfileId;
int lastPhase;
int buttonPressed;
int trimPressed;
bool startupFromFile;
bool deleteTempRadioData;
bool saveTempRadioData;
bool middleButtonPressed;
bool firstShow;
#ifdef JOYSTICKS
Joystick *joystick;
#endif
private slots: private slots:
virtual void mousePressEvent(QMouseEvent *event); virtual void mousePressEvent(QMouseEvent *event);
@ -145,12 +105,50 @@ class SimulatorWidget : public QWidget
virtual void wheelEvent(QWheelEvent *event); virtual void wheelEvent(QWheelEvent *event);
void onTimerEvent(); void onTimerEvent();
void onTrimPressed(int index); void onSimulatorStarted();
void onTrimReleased(int); void onSimulatorStopped();
void onTrimSliderMoved(int index, int value); void onSimulatorHeartbeat(qint32 loops, qint64 timestamp);
void centerSticks(); void onPhaseChanged(qint32 phase, const QString & name);
void onSimulatorError(const QString & error);
void onRadioWidgetValueChange(const RadioWidget::RadioWidgetType type, const int index, int value);
void onjoystickAxisValueChanged(int axis, int value); void onjoystickAxisValueChanged(int axis, int value);
void setRadioProfileId(int value);
void setupRadioWidgets();
void restoreRadioWidgetsState();
private:
void saveRadioWidgetsState(QList<QByteArray> & state);
Ui::SimulatorWidget * ui;
SimulatorInterface * simulator;
Firmware * firmware;
GeneralSettings radioSettings;
QTimer m_timer;
QString windowName;
QVector<Simulator::keymapHelp_t> keymapHelp;
QElapsedTimer m_heartbeatTimer;
SimulatedUIWidget * radioUiWidget;
VirtualJoystickWidget * vJoyLeft;
VirtualJoystickWidget * vJoyRight;
QVector<RadioWidget *> m_radioWidgets;
QString sdCardPath;
QString radioDataPath;
QByteArray startupData;
Board::Type m_board;
quint8 flags;
int radioProfileId;
bool startupFromFile;
bool deleteTempRadioData;
bool saveTempRadioData;
#ifdef JOYSTICKS
Joystick *joystick;
#endif
}; };
#endif // _SIMULATORWIDGET_H_ #endif // _SIMULATORWIDGET_H_

View file

@ -27,25 +27,13 @@
TelemetrySimulator::TelemetrySimulator(QWidget * parent, SimulatorInterface * simulator): TelemetrySimulator::TelemetrySimulator(QWidget * parent, SimulatorInterface * simulator):
QWidget(parent), QWidget(parent),
ui(new Ui::TelemetrySimulator), ui(new Ui::TelemetrySimulator),
simulator(simulator) simulator(simulator),
m_simuStarted(false),
m_telemEnable(false),
m_logReplayEnable(false)
{ {
ui->setupUi(this); ui->setupUi(this);
timer.setInterval(10);
connect(&timer, &QTimer::timeout, this, &TelemetrySimulator::generateTelemetryFrame);
connect(&logTimer, &QTimer::timeout, this, &TelemetrySimulator::onLogTimerEvent);
connect(ui->Simulate, SIGNAL(toggled(bool)), this, SLOT(onSimulateToggled(bool)));
connect(ui->loadLogFile, SIGNAL(released()), this, SLOT(onLoadLogFile()));
connect(ui->play, SIGNAL(released()), this, SLOT(onPlay()));
connect(ui->rewind, SIGNAL(clicked()), this, SLOT(onRewind()));
connect(ui->stepForward, SIGNAL(clicked()), this, SLOT(onStepForward()));
connect(ui->stepBack, SIGNAL(clicked()), this, SLOT(onStepBack()));
connect(ui->stop, SIGNAL(clicked()), this, SLOT(onStop()));
connect(ui->positionIndicator, SIGNAL(valueChanged(int)), this, SLOT(onPositionIndicatorChanged(int)));
connect(ui->replayRate, SIGNAL(valueChanged(int)), this, SLOT(onReplayRateChanged(int)));
ui->A1->setSpecialValueText(" "); ui->A1->setSpecialValueText(" ");
ui->A2->setSpecialValueText(" "); ui->A2->setSpecialValueText(" ");
ui->A3->setSpecialValueText(" "); ui->A3->setSpecialValueText(" ");
@ -57,7 +45,27 @@ TelemetrySimulator::TelemetrySimulator(QWidget * parent, SimulatorInterface * si
ui->A1_ratio->setEnabled(false); ui->A1_ratio->setEnabled(false);
ui->A2_ratio->setEnabled(false); ui->A2_ratio->setEnabled(false);
timer.setInterval(10);
connect(&timer, &QTimer::timeout, this, &TelemetrySimulator::generateTelemetryFrame);
connect(&logTimer, &QTimer::timeout, this, &TelemetrySimulator::onLogTimerEvent);
logPlayback = new LogPlaybackController(ui); logPlayback = new LogPlaybackController(ui);
connect(ui->Simulate, SIGNAL(toggled(bool)), this, SLOT(onSimulateToggled(bool)));
connect(ui->loadLogFile, SIGNAL(released()), this, SLOT(onLoadLogFile()));
connect(ui->play, SIGNAL(released()), this, SLOT(onPlay()));
connect(ui->rewind, SIGNAL(clicked()), this, SLOT(onRewind()));
connect(ui->stepForward, SIGNAL(clicked()), this, SLOT(onStepForward()));
connect(ui->stepBack, SIGNAL(clicked()), this, SLOT(onStepBack()));
connect(ui->stop, SIGNAL(clicked()), this, SLOT(onStop()));
connect(ui->positionIndicator, SIGNAL(valueChanged(int)), this, SLOT(onPositionIndicatorChanged(int)));
connect(ui->replayRate, SIGNAL(valueChanged(int)), this, SLOT(onReplayRateChanged(int)));
connect(this, &TelemetrySimulator::telemetryDataChanged, simulator, &SimulatorInterface::sendTelemetry);
connect(simulator, &SimulatorInterface::started, this, &TelemetrySimulator::onSimulatorStarted);
connect(simulator, &SimulatorInterface::stopped, this, &TelemetrySimulator::onSimulatorStopped);
} }
TelemetrySimulator::~TelemetrySimulator() TelemetrySimulator::~TelemetrySimulator()
@ -70,9 +78,15 @@ TelemetrySimulator::~TelemetrySimulator()
void TelemetrySimulator::onSimulatorStarted() void TelemetrySimulator::onSimulatorStarted()
{ {
m_simuStarted = true;
setupDataFields(); setupDataFields();
} }
void TelemetrySimulator::onSimulatorStopped()
{
m_simuStarted = false;
}
void TelemetrySimulator::onSimulateToggled(bool isChecked) void TelemetrySimulator::onSimulateToggled(bool isChecked)
{ {
if (isChecked) { if (isChecked) {
@ -149,13 +163,23 @@ void TelemetrySimulator::onReplayRateChanged(int value)
} }
} }
void TelemetrySimulator::closeEvent(QCloseEvent *event) void TelemetrySimulator::hideEvent(QHideEvent *event)
{ {
timer.stop(); m_telemEnable = ui->Simulate->isChecked();
logTimer.stop(); m_logReplayEnable = logTimer.isActive();
ui->Simulate->setChecked(false);
ui->stop->click();
event->accept(); event->accept();
} }
void TelemetrySimulator::showEvent(QShowEvent * event)
{
ui->Simulate->setChecked(m_telemEnable);
if (m_logReplayEnable)
ui->play->click();
}
#define SET_INSTANCE(control, id, def) ui->control->setText(QString::number(simulator->getSensorInstance(id, ((def) & 0x1F) + 1))) #define SET_INSTANCE(control, id, def) ui->control->setText(QString::number(simulator->getSensorInstance(id, ((def) & 0x1F) + 1)))
void TelemetrySimulator::setupDataFields() void TelemetrySimulator::setupDataFields()
@ -237,6 +261,9 @@ void TelemetrySimulator::generateTelemetryFrame()
static FlvssEmulator *flvss = new FlvssEmulator(); static FlvssEmulator *flvss = new FlvssEmulator();
static GPSEmulator *gps = new GPSEmulator(); static GPSEmulator *gps = new GPSEmulator();
if (!m_simuStarted)
return;
memset(buffer, 0, sizeof(buffer)); memset(buffer, 0, sizeof(buffer));
switch (item++) { switch (item++) {
@ -405,7 +432,7 @@ void TelemetrySimulator::generateTelemetryFrame()
} }
if (ok && (buffer[2] || buffer[3])) if (ok && (buffer[2] || buffer[3]))
simulator->sendTelemetry(buffer, FRSKY_SPORT_PACKET_SIZE); emit telemetryDataChanged(buffer, FRSKY_SPORT_PACKET_SIZE);
else else
generateTelemetryFrame(); generateTelemetryFrame();
} }

View file

@ -45,14 +45,19 @@ class TelemetrySimulator : public QWidget
explicit TelemetrySimulator(QWidget * parent, SimulatorInterface * simulator); explicit TelemetrySimulator(QWidget * parent, SimulatorInterface * simulator);
virtual ~TelemetrySimulator(); virtual ~TelemetrySimulator();
void onSimulatorStarted();
signals:
void telemetryDataChanged(uint8_t * data, unsigned int len);
protected slots: protected slots:
virtual void closeEvent(QCloseEvent *event); virtual void hideEvent(QHideEvent *event);
virtual void showEvent(QShowEvent *event);
void onSimulatorStarted();
void onSimulatorStopped();
void setupDataFields(); void setupDataFields();
void onSimulateToggled(bool isChecked); void onSimulateToggled(bool isChecked);
void generateTelemetryFrame();
void onLogTimerEvent(); void onLogTimerEvent();
void onLoadLogFile(); void onLoadLogFile();
void onPlay(); void onPlay();
@ -62,6 +67,7 @@ class TelemetrySimulator : public QWidget
void onStop(); void onStop();
void onPositionIndicatorChanged(int value); void onPositionIndicatorChanged(int value);
void onReplayRateChanged(int value); void onReplayRateChanged(int value);
void generateTelemetryFrame();
protected: protected:
@ -69,6 +75,9 @@ class TelemetrySimulator : public QWidget
QTimer timer; QTimer timer;
QTimer logTimer; QTimer logTimer;
SimulatorInterface *simulator; SimulatorInterface *simulator;
bool m_simuStarted;
bool m_telemEnable;
bool m_logReplayEnable;
// protected classes follow // protected classes follow
@ -81,7 +90,7 @@ class TelemetrySimulator : public QWidget
void play(); void play();
void stop(); void stop();
void rewind(); void rewind();
void stepForward(bool focusOnStop); void stepForward(bool focusOnStop = false);
void stepBack(); void stepBack();
void updatePositionLabel(int32_t percentage); void updatePositionLabel(int32_t percentage);
void setUiDataValues(); void setUiDataValues();

View file

@ -24,32 +24,40 @@
#include "helpers.h" #include "helpers.h"
#include "virtualjoystickwidget.h" #include "virtualjoystickwidget.h"
#define TRAINERSIMU_HEARTBEAT_PERIOD 500 // [ms]
TrainerSimulator::TrainerSimulator(QWidget * parent, SimulatorInterface * simulator): TrainerSimulator::TrainerSimulator(QWidget * parent, SimulatorInterface * simulator):
QWidget(parent), QWidget(parent),
ui(new Ui::TrainerSimulator), ui(new Ui::TrainerSimulator),
simulator(simulator) simulator(simulator),
m_simuStarted(false)
{ {
ui->setupUi(this); ui->setupUi(this);
vJoyLeft = new VirtualJoystickWidget(this, 'L', false); vJoyLeft = new VirtualJoystickWidget(this, 'L', false);
vJoyLeft->setStickColor(Qt::cyan); vJoyLeft->setStickColor(Qt::cyan);
vJoyLeft->setStickScale(512);
ui->leftStickLayout->addWidget(vJoyLeft); ui->leftStickLayout->addWidget(vJoyLeft);
vJoyRight = new VirtualJoystickWidget(this, 'R', false); vJoyRight = new VirtualJoystickWidget(this, 'R', false);
vJoyRight->setStickColor(Qt::cyan); vJoyRight->setStickColor(Qt::cyan);
vJoyRight->setStickScale(512);
ui->rightStickLayout->addWidget(vJoyRight); ui->rightStickLayout->addWidget(vJoyRight);
timer = new QTimer(this); connect(vJoyLeft, &VirtualJoystickWidget::valueChange, this, &TrainerSimulator::onRadioWidgetValueChange);
timer->setInterval(10); connect(vJoyRight, &VirtualJoystickWidget::valueChange, this, &TrainerSimulator::onRadioWidgetValueChange);
connect(timer, SIGNAL(timeout()), this, SLOT(onTimerEvent()));
connect(this, &TrainerSimulator::trainerHeartbeat, simulator, &SimulatorInterface::setTrainerTimeout);
connect(this, &TrainerSimulator::trainerChannelChange, simulator, &SimulatorInterface::setTrainerInput);
connect(simulator, &SimulatorInterface::started, this, &TrainerSimulator::onSimulatorStarted);
connect(simulator, &SimulatorInterface::stopped, this, &TrainerSimulator::onSimulatorStopped);
timer.setInterval(TRAINERSIMU_HEARTBEAT_PERIOD - 10);
connect(&timer, &QTimer::timeout, this, &TrainerSimulator::emitHeartbeat);
} }
TrainerSimulator::~TrainerSimulator() TrainerSimulator::~TrainerSimulator()
{ {
if (timer) {
timer->stop();
delete timer;
}
if (vJoyLeft) if (vJoyLeft)
delete vJoyLeft; delete vJoyLeft;
if (vJoyRight) if (vJoyRight)
@ -60,42 +68,44 @@ TrainerSimulator::~TrainerSimulator()
void TrainerSimulator::showEvent(QShowEvent *event) void TrainerSimulator::showEvent(QShowEvent *event)
{ {
timer->start(); start();
event->accept();
} }
void TrainerSimulator::closeEvent(QCloseEvent *event) void TrainerSimulator::hideEvent(QHideEvent *event)
{ {
timer->stop(); stop();
event->accept();
} }
void TrainerSimulator::centerSticks() void TrainerSimulator::start()
{ {
if (vJoyLeft) timer.start();
vJoyLeft->centerStick();
if (vJoyRight)
vJoyRight->centerStick();
} }
void TrainerSimulator::onTimerEvent() void TrainerSimulator::stop()
{ {
centerSticks(); timer.stop();
setTrainerInputs();
} }
void TrainerSimulator::setTrainerInputs() void TrainerSimulator::onSimulatorStarted()
{ {
if (!simulator) m_simuStarted = true;
return; }
if (vJoyLeft) { void TrainerSimulator::onSimulatorStopped()
simulator->setTrainerInput(0, int( 512 * vJoyLeft->getStickX())); // LEFT HORZ {
simulator->setTrainerInput(1, int(-512 * vJoyLeft->getStickY())); // LEFT VERT m_simuStarted = false;
} stop();
if (vJoyRight) { }
simulator->setTrainerInput(2, int(-512 * vJoyRight->getStickY())); // RGHT VERT
simulator->setTrainerInput(3, int( 512 * vJoyRight->getStickX())); // RGHT HORZ void TrainerSimulator::emitHeartbeat()
{
emit trainerHeartbeat(TRAINERSIMU_HEARTBEAT_PERIOD);
}
void TrainerSimulator::onRadioWidgetValueChange(RadioWidget::RadioWidgetType type, int index, int value)
{
if (type == RadioWidget::RADIO_WIDGET_STICK && m_simuStarted && timer.isActive()) {
emit trainerChannelChange(index, value);
emitHeartbeat();
} }
} }

View file

@ -26,6 +26,7 @@
#include <QCloseEvent> #include <QCloseEvent>
#include <QWidget> #include <QWidget>
#include <QTimer> #include <QTimer>
#include "radiowidget.h"
#include "simulatorinterface.h" #include "simulatorinterface.h"
namespace Ui { namespace Ui {
@ -42,21 +43,27 @@ class TrainerSimulator : public QWidget
explicit TrainerSimulator(QWidget * parent, SimulatorInterface * simulator); explicit TrainerSimulator(QWidget * parent, SimulatorInterface * simulator);
virtual ~TrainerSimulator(); virtual ~TrainerSimulator();
signals:
void trainerHeartbeat(quint16 ms);
void trainerChannelChange(quint8 index, qint16 value);
protected slots: protected slots:
void start();
void stop();
virtual void showEvent(QShowEvent *event); virtual void showEvent(QShowEvent *event);
virtual void closeEvent(QCloseEvent *event); virtual void hideEvent(QHideEvent *event);
void centerSticks(); void onSimulatorStarted();
void setTrainerInputs(); void onSimulatorStopped();
void onTimerEvent(); void onRadioWidgetValueChange(RadioWidget::RadioWidgetType type, int index, int value);
void emitHeartbeat();
protected: protected:
Ui::TrainerSimulator * ui; Ui::TrainerSimulator * ui;
QTimer * timer;
SimulatorInterface *simulator; SimulatorInterface *simulator;
VirtualJoystickWidget * vJoyLeft; VirtualJoystickWidget * vJoyLeft;
VirtualJoystickWidget * vJoyRight; VirtualJoystickWidget * vJoyRight;
QTimer timer;
bool m_simuStarted;
}; };

View file

@ -22,47 +22,12 @@
#define _BUTTONSWIDGET_H_ #define _BUTTONSWIDGET_H_
#include "radiouiaction.h" #include "radiouiaction.h"
#include "radiokeywidget.h"
#include <QWidget> #include <QWidget>
#include <QtGui> #include <QtGui>
#include <QStyleOption> #include <QStyleOption>
class Area : public QObject
{
Q_OBJECT
public:
explicit Area(const QPolygon & polygon, const QString &image, RadioUiAction * action = NULL, QObject * parent = NULL):
QObject(parent),
polygon(polygon),
imgFile(image),
action(action)
{
if (action)
connect(action, &RadioUiAction::toggled, this, &Area::onActionToggled);
}
bool contains(int x, int y)
{
return polygon.containsPoint(QPoint(x, y), Qt::OddEvenFill);
}
void onActionToggled(int, bool active)
{
if (active)
emit imageChanged(imgFile);
else
emit imageChanged("");
}
QPolygon polygon;
QString imgFile;
RadioUiAction * action;
signals:
void imageChanged(const QString image);
};
class ButtonsWidget : public QWidget class ButtonsWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
@ -80,18 +45,17 @@ class ButtonsWidget : public QWidget
QWidget::setStyleSheet(sheet); QWidget::setStyleSheet(sheet);
} }
void addArea(int x1, int y1, int x2, int y2, const char * image, RadioUiAction * action = NULL) RadioKeyWidget * addArea(const QRect & rect, const char * image, RadioUiAction * action = NULL)
{ {
QPolygon polygon; return addArea(QPolygon(rect), image, action);
polygon.setPoints(4, x1, y1, x1, y2, x2, y2, x2, y1);
addArea(polygon, image, action);
} }
void addArea(const QPolygon & polygon, const char * image, RadioUiAction * action = NULL) RadioKeyWidget * addArea(const QPolygon & polygon, const char * image, RadioUiAction * action = NULL)
{ {
Area * area = new Area(polygon, image, action, this); RadioKeyWidget * btn = new RadioKeyWidget(polygon, image, action, this);
areas.push_back(area); m_buttons.append(btn);
connect(area, &Area::imageChanged, this, &ButtonsWidget::setBitmap); connect(btn, &RadioKeyWidget::imageChanged, this, &ButtonsWidget::setBitmap);
return btn;
} }
protected: protected:
@ -113,16 +77,14 @@ class ButtonsWidget : public QWidget
return; return;
} }
foreach(Area * area, areas) { foreach(RadioKeyWidget * key, m_buttons) {
if (!area->action) if (key->contains(event->pos())) {
continue; key->toggle(press);
if (area->contains(event->x(), event->y())) {
area->action->trigger(press);
event->accept(); event->accept();
return; return;
} }
else if (area->action->isActive()) { else if (key->getValue()) {
area->action->trigger(false); key->toggle(false);
} }
} }
event->ignore(); event->ignore();
@ -145,7 +107,7 @@ class ButtonsWidget : public QWidget
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
} }
QList<Area *> areas; QList<RadioKeyWidget *> m_buttons;
QString defaultStyleSheet; QString defaultStyleSheet;
}; };

View file

@ -0,0 +1,100 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*/
#ifndef RADIOKEYWIDGET_H
#define RADIOKEYWIDGET_H
#include "radiouiaction.h"
#include "radiowidget.h"
class RadioKeyWidget : public RadioWidget
{
Q_OBJECT
public:
explicit RadioKeyWidget(const QPolygon & polygon, const QString &image, RadioUiAction * action = NULL, QWidget * parent = NULL, Qt::WindowFlags f = Qt::WindowFlags()):
RadioWidget(action, parent, f),
polygon(polygon),
imgFile(image)
{
m_type = RADIO_WIDGET_KEY;
setValue(0);
hide(); // we're a "virtual" button for now
setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
}
virtual void setAction(RadioUiAction * action)
{
if (m_action)
disconnect(m_action, 0, this, 0);
RadioWidget::setAction(action);
if (m_action)
connect(m_action, &RadioUiAction::toggled, this, &RadioKeyWidget::onActionToggled);
}
virtual int getValue() const
{
return (m_action && m_action->isActive()) ? 1 : 0;
}
void press() {
if (m_action)
m_action->trigger(true);
}
void release() {
if (m_action)
m_action->trigger(false);
}
void toggle(bool down)
{
if (down)
press();
else
release();
}
bool contains(const QPoint & point)
{
return polygon.containsPoint(point, Qt::OddEvenFill);
}
signals:
void imageChanged(const QString image);
protected:
virtual void onActionToggled(int index, bool active)
{
RadioWidget::onActionToggled(index, active);
emit imageChanged(active ? imgFile : "");
}
QPolygon polygon;
QString imgFile;
};
#endif // RADIOKEYWIDGET_H

View file

@ -26,7 +26,7 @@
#include <QDial> #include <QDial>
#include <QMouseEvent> #include <QMouseEvent>
#include <QDebug> #include <QToolTip>
#include <math.h> #include <math.h>
class RadioKnobWidget : public RadioWidget class RadioKnobWidget : public RadioWidget
@ -65,7 +65,6 @@ class RadioKnobWidget : public RadioWidget
Board::PotType m_potType; Board::PotType m_potType;
class KnobWidget : public QDial class KnobWidget : public QDial
{ {
public: public:
@ -73,6 +72,8 @@ class RadioKnobWidget : public RadioWidget
explicit KnobWidget(Board::PotType type, QWidget * parent = 0): explicit KnobWidget(Board::PotType type, QWidget * parent = 0):
QDial(parent) QDial(parent)
{ {
m_toolTip = tr("<p>Value (input): <b>%1</b></p>");
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
setFixedSize(QSize(42, 42)); setFixedSize(QSize(42, 42));
setNotchesVisible(true); setNotchesVisible(true);
@ -88,7 +89,7 @@ class RadioKnobWidget : public RadioWidget
} }
else { else {
m_stepSize = 1; m_stepSize = 1;
setToolTip(tr("Right-double-click to reset to center.")); m_toolTip.append(tr("Right-double-click to reset to center."));
setMinimum(-1024); setMinimum(-1024);
setMaximum(1024); setMaximum(1024);
setPageStep(128); setPageStep(128);
@ -104,6 +105,19 @@ class RadioKnobWidget : public RadioWidget
QDial::setValue(value); QDial::setValue(value);
} }
bool event(QEvent *event)
{
if (event->type() == QEvent::ToolTip) {
QHelpEvent * helpEvent = static_cast<QHelpEvent *>(event);
if (helpEvent) {
QToolTip::showText(helpEvent->globalPos(), m_toolTip.arg(this->value()));
return true;
}
}
return QWidget::event(event);
}
void mousePressEvent(QMouseEvent * event) void mousePressEvent(QMouseEvent * event)
{ {
if (m_stepSize == 1 && event->button() == Qt::RightButton && event->type() == QEvent::MouseButtonDblClick) { if (m_stepSize == 1 && event->button() == Qt::RightButton && event->type() == QEvent::MouseButtonDblClick) {
@ -129,6 +143,7 @@ class RadioKnobWidget : public RadioWidget
} }
quint16 m_stepSize; quint16 m_stepSize;
QString m_toolTip;
}; };
}; };

View file

@ -110,9 +110,8 @@ class RadioSwitchWidget : public RadioWidget
void setToggleLocked(bool lock) void setToggleLocked(bool lock)
{ {
if (m_flags != (quint16)lock) { if (m_flags != (quint16)lock) {
m_flags = lock; setFlags((quint16)lock);
setValue((int)lock); setValue((int)lock);
emit flagsChanged(m_flags);
} }
} }

View file

@ -22,6 +22,7 @@
#define _RADIOTRIMWIDGET_H_ #define _RADIOTRIMWIDGET_H_
#include "radiowidget.h" #include "radiowidget.h"
#include "boards.h"
#include "sliderwidget.h" #include "sliderwidget.h"
#include <QToolButton> #include <QToolButton>
@ -38,12 +39,6 @@ class RadioTrimWidget : public RadioWidget
{ {
init(orientation); init(orientation);
} }
explicit RadioTrimWidget(const QString & labelText, Qt::Orientation orientation = Qt::Vertical, int value = 0, QWidget * parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()) :
RadioWidget(labelText, value, parent, f),
m_slider(NULL)
{
init(orientation);
}
void setIndices(int sliderIdx = -1, int decrBtnIdx = -1, int incrBtnIdx = -1) void setIndices(int sliderIdx = -1, int decrBtnIdx = -1, int incrBtnIdx = -1)
{ {
@ -58,11 +53,27 @@ class RadioTrimWidget : public RadioWidget
m_slider->setRange(min, max); m_slider->setRange(min, max);
} }
signals: void setTrimRangeQual(const int index, const int min, const int max)
{
if (index == m_index || index == Board::TRIM_AXIS_COUNT)
setTrimRange(min, max);
}
void trimButtonPressed(int index); void setTrimValue(const int index, const int value)
void trimButtonReleased(int index); {
void trimSliderMoved(int index, int value); if (index == m_index || index == Board::TRIM_AXIS_COUNT)
setValue(value);
}
void setValue(const int & value)
{
if (sender() && qobject_cast<SliderWidget *>(sender())) {
RadioWidget::setValue(value);
}
else if (m_slider) {
m_slider->setValue(value);
}
}
protected slots: protected slots:
@ -75,20 +86,20 @@ class RadioTrimWidget : public RadioWidget
setTrimRange(-125, 125); setTrimRange(-125, 125);
setIndices(); setIndices();
QSize btnIcnSz(12, 12); const QSize btnSz(18, 18);
QWidget * trimWidget = new QWidget(this); QWidget * trimWidget = new QWidget(this);
QBoxLayout * trimLayout = new QVBoxLayout(trimWidget); QBoxLayout * trimLayout = new QVBoxLayout(trimWidget);
trimLayout->setSpacing(5); trimLayout->setSpacing(4);
trimLayout->setContentsMargins(8, 8, 8, 8);
QToolButton * trimBtnInc = new QToolButton(trimWidget); QToolButton * trimBtnInc = new QToolButton(trimWidget);
trimBtnInc->setIconSize(btnIcnSz); trimBtnInc->setMaximumSize(btnSz);
QToolButton * trimBtnDec = new QToolButton(trimWidget); QToolButton * trimBtnDec = new QToolButton(trimWidget);
trimBtnDec->setIconSize(btnIcnSz); trimBtnDec->setMaximumSize(btnSz);
Qt::Alignment algn; Qt::Alignment algn;
if (orientation == Qt::Horizontal) { if (orientation == Qt::Horizontal) {
trimWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); trimWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
trimLayout->setDirection(QBoxLayout::RightToLeft); trimLayout->setDirection(QBoxLayout::RightToLeft);
trimLayout->setContentsMargins(0, 8, 0, 8);
trimBtnInc->setArrowType(Qt::RightArrow); trimBtnInc->setArrowType(Qt::RightArrow);
trimBtnDec->setArrowType(Qt::LeftArrow); trimBtnDec->setArrowType(Qt::LeftArrow);
algn = Qt::AlignVCenter; algn = Qt::AlignVCenter;
@ -96,6 +107,7 @@ class RadioTrimWidget : public RadioWidget
else { else {
trimWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); trimWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
trimLayout->setDirection(QBoxLayout::TopToBottom); trimLayout->setDirection(QBoxLayout::TopToBottom);
trimLayout->setContentsMargins(8, 0, 8, 0);
trimBtnInc->setArrowType(Qt::UpArrow); trimBtnInc->setArrowType(Qt::UpArrow);
trimBtnDec->setArrowType(Qt::DownArrow); trimBtnDec->setArrowType(Qt::DownArrow);
algn = Qt::AlignHCenter; algn = Qt::AlignHCenter;
@ -107,21 +119,11 @@ class RadioTrimWidget : public RadioWidget
setWidget(trimWidget, algn); setWidget(trimWidget, algn);
connect(m_slider, &SliderWidget::valueChanged, this, &RadioWidget::setValue); connect(m_slider, &SliderWidget::valueChanged, this, &RadioTrimWidget::setValue);
connect(this, &RadioWidget::valueChanged, this, &RadioTrimWidget::onValueChanged); connect(trimBtnInc, &QToolButton::pressed, [this]() { emit valueChange(m_type, m_btnIncIndex, RADIO_TRIM_BTN_ON); });
connect(trimBtnInc, &QToolButton::released, [this]() { emit valueChange(m_type, m_btnIncIndex, RADIO_TRIM_BTN_OFF); });
connect(trimBtnInc, &QToolButton::pressed, [this]() { emit trimButtonPressed(m_btnIncIndex); }); connect(trimBtnDec, &QToolButton::pressed, [this]() { emit valueChange(m_type, m_btnDecIndex, RADIO_TRIM_BTN_ON); });
connect(trimBtnInc, &QToolButton::released, [this]() { emit trimButtonReleased(m_btnIncIndex); }); connect(trimBtnDec, &QToolButton::released, [this]() { emit valueChange(m_type, m_btnDecIndex, RADIO_TRIM_BTN_OFF); });
connect(trimBtnDec, &QToolButton::pressed, [this]() { emit trimButtonPressed(m_btnDecIndex); });
connect(trimBtnDec, &QToolButton::released, [this]() { emit trimButtonReleased(m_btnDecIndex); });
}
void onValueChanged(int value)
{
m_slider->blockSignals(true);
m_slider->setValue(value);
m_slider->blockSignals(false);
emit trimSliderMoved(m_index, m_value);
} }
protected: protected:
@ -130,5 +132,4 @@ class RadioTrimWidget : public RadioWidget
int m_btnIncIndex; int m_btnIncIndex;
}; };
#endif // _RADIOTRIMWIDGET_H_ #endif // _RADIOTRIMWIDGET_H_

View file

@ -18,51 +18,66 @@
* GNU General Public License for more details. * GNU General Public License for more details.
*/ */
#include "radiouiaction.h"
#include "radiowidget.h" #include "radiowidget.h"
RadioWidget::RadioWidget(QWidget * parent, Qt::WindowFlags f) : RadioWidget::RadioWidget(QWidget * parent, Qt::WindowFlags f) :
QWidget(parent, f), QWidget(parent, f),
m_gridLayout(NULL),
m_controlWidget(NULL),
m_nameLabel(NULL),
m_action(NULL),
m_value(0), m_value(0),
m_flags(0), m_flags(0),
m_invertValue(false), m_valueReset(false),
m_showLabel(false), m_showLabel(false),
m_labelText(""), m_labelText("")
m_type(RADIO_WIDGET_NONE)
{ {
init(); qRegisterMetaType<RadioWidgetState>();
setIndex(-1);
setType(RADIO_WIDGET_NONE);
}
RadioWidget::RadioWidget(RadioUiAction * action, QWidget * parent, Qt::WindowFlags f) :
RadioWidget(parent, f)
{
setAction(action);
} }
RadioWidget::RadioWidget(const QString & labelText, int value, QWidget * parent, Qt::WindowFlags f) : RadioWidget::RadioWidget(const QString & labelText, int value, QWidget * parent, Qt::WindowFlags f) :
QWidget(parent, f), RadioWidget(parent, f)
m_value(value),
m_flags(0),
m_invertValue(false),
m_showLabel(true),
m_labelText(labelText),
m_type(RADIO_WIDGET_NONE)
{ {
init(); setValue(value);
setLabelText(labelText);
} }
void RadioWidget::setIndex(int index) void RadioWidget::setIndex(const int & index)
{ {
m_index = index; m_index = index;
} }
void RadioWidget::setInvertValue(bool invertValue) void RadioWidget::setType(const RadioWidgetType & type)
{ {
m_invertValue = invertValue; m_type = type;
} }
void RadioWidget::setValue(int value) void RadioWidget::setValue(const int & value)
{ {
if (value != m_value) { if (value != m_value || m_valueReset) {
m_value = value; m_value = value;
m_valueReset = false;
emit valueChanged(value); emit valueChanged(value);
emit valueChange(m_type, m_index, value);
} }
} }
void RadioWidget::setFlags(quint16 flags) void RadioWidget::setValueQual(const RadioWidget::RadioWidgetType & type, const int & index, const int & value)
{
if (type == m_type && index == m_index)
setValue(value);
}
void RadioWidget::setFlags(const quint16 & flags)
{ {
if (flags != m_flags) { if (flags != m_flags) {
m_flags = flags; m_flags = flags;
@ -70,7 +85,7 @@ void RadioWidget::setFlags(quint16 flags)
} }
} }
void RadioWidget::setShowLabel(bool show) void RadioWidget::setShowLabel(const bool show)
{ {
if (show != m_showLabel) { if (show != m_showLabel) {
m_showLabel = show; m_showLabel = show;
@ -85,13 +100,12 @@ void RadioWidget::setLabelText(const QString & labelText, bool showLabel)
addLabel(); addLabel();
} }
void RadioWidget::setStateData(const QByteArray & data) void RadioWidget::setStateData(const RadioWidgetState & state)
{ {
RadioWidgetState state; if (state.type != m_type || state.index != m_index)
QDataStream stream(data); return;
stream >> state;
//setIndex(state.index); m_valueReset = true;
setValue(state.value); setValue(state.value);
setFlags(state.flags); setFlags(state.flags);
} }
@ -109,9 +123,26 @@ void RadioWidget::changeVisibility(bool visible)
} }
} }
void RadioWidget::setAction(RadioUiAction * action)
{
if (m_action) {
disconnect(m_action, 0, this, 0);
delete m_action;
m_action = NULL;
}
m_action = action;
if (m_action) {
if (m_action->getIndex() > -1)
setIndex(m_action->getIndex());
connect(m_action, &RadioUiAction::toggled, this, &RadioWidget::onActionToggled);
}
}
int RadioWidget::getValue() const int RadioWidget::getValue() const
{ {
return m_value * (m_invertValue ? -1 : 1); return m_value;
} }
int RadioWidget::getIndex() const int RadioWidget::getIndex() const
@ -134,25 +165,34 @@ QByteArray RadioWidget::getStateData() const
RadioWidget::RadioWidgetState RadioWidget::getState() const RadioWidget::RadioWidgetState RadioWidget::getState() const
{ {
return RadioWidgetState(quint8(m_type), qint8(m_index), qint16(m_value), quint16(m_flags));; return RadioWidgetState(quint8(m_type), qint8(m_index), qint16(m_value), quint16(m_flags));
} }
void RadioWidget::init() RadioUiAction * RadioWidget::getAction() const
{ {
setIndex(0); return m_action;
m_controlWidget = NULL; }
m_nameLabel = NULL;
m_gridLayout= new QGridLayout(this); void RadioWidget::onActionToggled(int index, bool active)
{
emit valueChange(m_type, index, getValue());
}
void RadioWidget::addLayout()
{
if (m_gridLayout)
return;
m_gridLayout = new QGridLayout(this);
m_gridLayout->setContentsMargins(0, 0, 0, 0); m_gridLayout->setContentsMargins(0, 0, 0, 0);
m_gridLayout->setVerticalSpacing(4); m_gridLayout->setVerticalSpacing(4);
m_gridLayout->setHorizontalSpacing(0); m_gridLayout->setHorizontalSpacing(0);
addLabel();
} }
void RadioWidget::addLabel() void RadioWidget::addLabel()
{ {
addLayout();
if (m_nameLabel) { if (m_nameLabel) {
m_gridLayout->removeWidget(m_nameLabel); m_gridLayout->removeWidget(m_nameLabel);
m_nameLabel->deleteLater(); m_nameLabel->deleteLater();
@ -168,6 +208,7 @@ void RadioWidget::addLabel()
void RadioWidget::setWidget(QWidget * widget, Qt::Alignment align) void RadioWidget::setWidget(QWidget * widget, Qt::Alignment align)
{ {
addLayout();
if (m_controlWidget) { if (m_controlWidget) {
m_gridLayout->removeWidget(m_controlWidget); m_gridLayout->removeWidget(m_controlWidget);
m_controlWidget->deleteLater(); m_controlWidget->deleteLater();

View file

@ -28,6 +28,8 @@
#define RADIO_WIDGET_STATE_VERSION 1 #define RADIO_WIDGET_STATE_VERSION 1
class RadioUiAction;
class RadioWidget : public QWidget class RadioWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
@ -41,10 +43,14 @@ class RadioWidget : public QWidget
RADIO_WIDGET_SWITCH, RADIO_WIDGET_SWITCH,
RADIO_WIDGET_KNOB, RADIO_WIDGET_KNOB,
RADIO_WIDGET_FADER, RADIO_WIDGET_FADER,
RADIO_WIDGET_TRIM, RADIO_WIDGET_TRIM, // trim axis, usually 2 buttons & slider
RADIO_WIDGET_STICK // actually one axis of a stick RADIO_WIDGET_STICK, // actually one axis of a stick
RADIO_WIDGET_KEY, // UI key/pushbutton
RADIO_WIDGET_ENUM_COUNT
}; };
enum RadioTrimWidgetButtonStates { RADIO_TRIM_BTN_ON = 1024, RADIO_TRIM_BTN_OFF = -1024 };
struct RadioWidgetState { struct RadioWidgetState {
public: public:
RadioWidgetState(quint8 type = 0, qint8 index = 0, qint16 value = 0, quint16 flags = 0) : RadioWidgetState(quint8 type = 0, qint8 index = 0, qint16 value = 0, quint16 flags = 0) :
@ -65,49 +71,58 @@ class RadioWidget : public QWidget
quint8 _version = RADIO_WIDGET_STATE_VERSION; // structure definition version quint8 _version = RADIO_WIDGET_STATE_VERSION; // structure definition version
}; };
explicit RadioWidget(QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()); explicit RadioWidget(QWidget * parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
explicit RadioWidget(const QString &labelText, int value = 0, QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()); explicit RadioWidget(RadioUiAction * action = NULL, QWidget * parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
explicit RadioWidget(const QString & labelText, int value = 0, QWidget * parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
void setIndex(int index);
void setInvertValue(bool invertValue);
void setValue(int value);
void setFlags(quint16 flags);
void setShowLabel(bool show);
void setLabelText(const QString &labelText, bool showLabel = true);
void setStateData(const QByteArray & data);
void changeVisibility(bool visible);
virtual int getValue() const; virtual int getValue() const;
int getIndex() const; virtual int getIndex() const;
int getType() const; virtual int getType() const;
QByteArray getStateData() const; virtual QByteArray getStateData() const;
RadioWidgetState getState() const; virtual RadioWidgetState getState() const;
virtual RadioUiAction * getAction() const;
public slots:
virtual void setIndex(const int & index);
virtual void setType(const RadioWidgetType & type);
virtual void setValue(const int & value);
virtual void setValueQual(const RadioWidgetType & type, const int & index, const int & value);
virtual void setFlags(const quint16 & flags);
virtual void setShowLabel(const bool show);
virtual void setLabelText(const QString & labelText, bool showLabel = true);
virtual void setStateData(const RadioWidgetState & state);
virtual void changeVisibility(bool visible);
virtual void setAction(RadioUiAction * action);
signals:
void valueChange(const RadioWidgetType type, const int index, int value);
void valueChanged(const int value);
void flagsChanged(const quint16 flags);
protected: protected:
void init(); void addLayout();
void addLabel(); void addLabel();
void setWidget(QWidget * widget = NULL, Qt::Alignment align = Qt::AlignHCenter); void setWidget(QWidget * widget = NULL, Qt::Alignment align = Qt::AlignHCenter);
virtual void onActionToggled(int index, bool active);
int m_value;
int m_index;
quint16 m_flags;
bool m_invertValue;
bool m_showLabel;
QString m_labelText;
RadioWidgetType m_type;
private:
QGridLayout * m_gridLayout; QGridLayout * m_gridLayout;
QWidget * m_controlWidget; QWidget * m_controlWidget;
QLabel * m_nameLabel; QLabel * m_nameLabel;
RadioUiAction * m_action;
signals: int m_value;
int m_index;
void valueChanged(int value); quint16 m_flags;
void flagsChanged(quint16 flags); bool m_valueReset;
bool m_showLabel;
QString m_labelText;
RadioWidgetType m_type;
}; };
Q_DECLARE_METATYPE(RadioWidget::RadioWidgetState)
#endif // _RADIOWIDGET_H_ #endif // _RADIOWIDGET_H_

View file

@ -23,6 +23,7 @@
#include <QtGui> #include <QtGui>
#include <QSlider> #include <QSlider>
#include <QToolTip>
class SliderWidget : public QSlider class SliderWidget : public QSlider
{ {
@ -33,11 +34,23 @@ class SliderWidget : public QSlider
explicit SliderWidget(QWidget * parent = 0): explicit SliderWidget(QWidget * parent = 0):
QSlider(parent) QSlider(parent)
{ {
setToolTip(tr("Right-double-click to reset to center.")); m_toolTip = tr("<p>Value (input): <b>%1</b></p>") % tr("Right-double-click to reset to center.");
} }
protected: protected:
bool event(QEvent *event)
{
if (event->type() == QEvent::ToolTip) {
QHelpEvent * helpEvent = static_cast<QHelpEvent *>(event);
if (helpEvent) {
QToolTip::showText(helpEvent->globalPos(), m_toolTip.arg(this->value()));
return true;
}
}
return QWidget::event(event);
}
void mousePressEvent(QMouseEvent * event) void mousePressEvent(QMouseEvent * event)
{ {
if (event->button() == Qt::RightButton && event->type() == QEvent::MouseButtonDblClick) { if (event->button() == Qt::RightButton && event->type() == QEvent::MouseButtonDblClick) {
@ -47,6 +60,8 @@ class SliderWidget : public QSlider
} }
QSlider::mousePressEvent(event); QSlider::mousePressEvent(event);
} }
QString m_toolTip;
}; };
#endif // _SLIDERWIDGET_H_ #endif // _SLIDERWIDGET_H_

View file

@ -21,11 +21,11 @@
#define GBALL_SIZE 25 #define GBALL_SIZE 25
#define GBALL_SIZE_MN 20 #define GBALL_SIZE_MN 20
#define GBALL_SIZE_MX 35 #define GBALL_SIZE_MX 35
#define CENTER_INTERVAL 50 // ms
#include "virtualjoystickwidget.h" #include "virtualjoystickwidget.h"
#include "boards.h" #include "boards.h"
#include "constants.h"
#include "modeledit/node.h" #include "modeledit/node.h"
#include "helpers.h" #include "helpers.h"
#include "radiotrimwidget.h" #include "radiotrimwidget.h"
@ -43,6 +43,7 @@ VirtualJoystickWidget::VirtualJoystickWidget(QWidget *parent, QChar side, bool s
btnFixY(NULL), btnFixY(NULL),
nodeLabelX(NULL), nodeLabelX(NULL),
nodeLabelY(NULL), nodeLabelY(NULL),
m_stickScale(1024),
m_stickPressed(false) m_stickPressed(false)
{ {
ar = (float)size.width() / size.height(); ar = (float)size.width() / size.height();
@ -135,12 +136,18 @@ VirtualJoystickWidget::VirtualJoystickWidget(QWidget *parent, QChar side, bool s
layout->addItem(new QSpacerItem(0, 0), 1, 4, 2, 1); // r1-2 c4: right h spacer layout->addItem(new QSpacerItem(0, 0), 1, 4, 2, 1); // r1-2 c4: right h spacer
layout->addItem(new QSpacerItem(0, 0), 3, 0, 1, 5); // r4 c0-4: bot v spacer layout->addItem(new QSpacerItem(0, 0), 3, 0, 1, 5); // r4 c0-4: bot v spacer
connect(node, &Node::xChanged, this, &VirtualJoystickWidget::updateNodeValueLabels); setStickIndices(getStickIndex('H'), getStickIndex('V'));
connect(node, &Node::yChanged, this, &VirtualJoystickWidget::updateNodeValueLabels);
connect(node, &Node::xChanged, this, &VirtualJoystickWidget::onNodeXChanged);
connect(node, &Node::yChanged, this, &VirtualJoystickWidget::onNodeYChanged);
connect(scene, &CustomGraphicsScene::mouseEvent, this, &VirtualJoystickWidget::onGsMouseEvent); connect(scene, &CustomGraphicsScene::mouseEvent, this, &VirtualJoystickWidget::onGsMouseEvent);
setSize(prefSize, frameSize()); setSize(prefSize, frameSize());
centerStickTimer.setInterval(CENTER_INTERVAL);
connect(&centerStickTimer, &QTimer::timeout, this, &VirtualJoystickWidget::centerStick);
centerStickTimer.start();
} }
void VirtualJoystickWidget::setStickX(qreal x) void VirtualJoystickWidget::setStickX(qreal x)
@ -158,6 +165,31 @@ void VirtualJoystickWidget::setStickPos(QPointF xy)
node->setPos(xy); node->setPos(xy);
} }
void VirtualJoystickWidget::setStickAxisValue(int index, int value)
{
using namespace Board;
qreal rvalue = value / 1024.0f;
switch (index) {
case STICK_AXIS_LH :
if (stickSide == 'L')
setStickX(rvalue);
break;
case STICK_AXIS_RH :
if (stickSide == 'R')
setStickX(rvalue);
break;
case STICK_AXIS_LV :
if (stickSide == 'L')
setStickY(rvalue);
break;
case STICK_AXIS_RV :
if (stickSide == 'R')
setStickY(rvalue);
break;
}
}
void VirtualJoystickWidget::centerStick() void VirtualJoystickWidget::centerStick()
{ {
node->stepToCenter(); node->stepToCenter();
@ -178,20 +210,55 @@ QPointF VirtualJoystickWidget::getStickPos()
return QPointF(node->getX(), node->getY()); return QPointF(node->getX(), node->getY());
} }
void VirtualJoystickWidget::setTrimValue(int which, int value) void VirtualJoystickWidget::setTrimsRange(int min, int max)
{ {
RadioTrimWidget * trim = getTrimWidget(which); if (hTrimWidget)
hTrimWidget->setTrimRange(min, max);
if (vTrimWidget)
vTrimWidget->setTrimRange(min, max);
}
void VirtualJoystickWidget::setTrimsRange(int rng)
{
setTrimsRange(-rng, rng);
}
void VirtualJoystickWidget::setTrimRange(int index, int min, int max)
{
if (index == Board::TRIM_AXIS_COUNT) {
setTrimsRange(min, max);
return;
}
RadioTrimWidget * trim = getTrimWidget(index);
if (trim) {
trim->setTrimRange(min, max);
}
}
void VirtualJoystickWidget::setTrimValue(int index, int value)
{
RadioTrimWidget * trim = getTrimWidget(index);
if (trim) { if (trim) {
trim->setValue(value); trim->setValue(value);
} }
} }
void VirtualJoystickWidget::setTrimRange(int which, int min, int max) int VirtualJoystickWidget::getStickScale() const
{ {
RadioTrimWidget * trim = getTrimWidget(which); return m_stickScale;
if (trim) { }
trim->setTrimRange(min, max);
} void VirtualJoystickWidget::setStickScale(int stickScale)
{
m_stickScale = stickScale;
}
void VirtualJoystickWidget::setWidgetValue(const RadioWidget::RadioWidgetType type, const int index, const int value)
{
if (type == RadioWidget::RADIO_WIDGET_TRIM)
setTrimValue(index, value);
else if (type == RadioWidget::RADIO_WIDGET_STICK)
setStickAxisValue(index, value);
} }
int VirtualJoystickWidget::getTrimValue(int which) int VirtualJoystickWidget::getTrimValue(int which)
@ -203,27 +270,25 @@ int VirtualJoystickWidget::getTrimValue(int which)
return 0; return 0;
} }
void VirtualJoystickWidget::setStickConstraint(int which, bool active) void VirtualJoystickWidget::setStickIndices(int xIdx, int yIdx)
{ {
if (btnHoldX == NULL) m_xIndex = xIdx;
m_yIndex = yIdx;
}
void VirtualJoystickWidget::setStickConstraint(quint8 index, bool active)
{
if (btnHoldX == NULL || !index)
return; // no buttons return; // no buttons
switch (which) { if (index & HOLD_X)
case HOLD_X:
btnHoldX->setChecked(active); btnHoldX->setChecked(active);
break; if (index & HOLD_Y)
case HOLD_Y:
btnHoldY->setChecked(active); btnHoldY->setChecked(active);
break; if (index & FIX_X)
case FIX_X:
btnFixX->setChecked(active); btnFixX->setChecked(active);
break; if (index & FIX_Y)
case FIX_Y:
btnFixY->setChecked(active); btnFixY->setChecked(active);
break;
default:
break;
}
} }
void VirtualJoystickWidget::setStickColor(const QColor & color) void VirtualJoystickWidget::setStickColor(const QColor & color)
@ -231,6 +296,15 @@ void VirtualJoystickWidget::setStickColor(const QColor & color)
node->setColor(color); node->setColor(color);
} }
void VirtualJoystickWidget::loadDefaultsForMode(const unsigned mode)
{
if (((mode & 1) && stickSide == 'L') || (!(mode & 1) && stickSide == 'R')) {
setStickConstraint(HOLD_Y, true);
setStickY(1.0);
onNodeYChanged();
}
}
QSize VirtualJoystickWidget::sizeHint() const { QSize VirtualJoystickWidget::sizeHint() const {
return prefSize; return prefSize;
} }
@ -241,6 +315,18 @@ void VirtualJoystickWidget::resizeEvent(QResizeEvent * event)
setSize(event->size(), event->oldSize()); setSize(event->size(), event->oldSize());
} }
void VirtualJoystickWidget::onNodeXChanged()
{
emit valueChange(RadioWidget::RADIO_WIDGET_STICK, m_xIndex, int(m_stickScale * getStickX()));
updateNodeValueLabels();
}
void VirtualJoystickWidget::onNodeYChanged()
{
emit valueChange(RadioWidget::RADIO_WIDGET_STICK, m_yIndex, int(-m_stickScale * getStickY()));
updateNodeValueLabels();
}
void VirtualJoystickWidget::setSize(const QSize & size, const QSize &) void VirtualJoystickWidget::setSize(const QSize & size, const QSize &)
{ {
float thisAspectRatio = (float)size.width() / size.height(); float thisAspectRatio = (float)size.width() / size.height();
@ -301,9 +387,7 @@ RadioTrimWidget * VirtualJoystickWidget::createTrimWidget(QChar type)
RadioTrimWidget * trimWidget = new RadioTrimWidget(type == 'H' ? Qt::Horizontal : Qt::Vertical); RadioTrimWidget * trimWidget = new RadioTrimWidget(type == 'H' ? Qt::Horizontal : Qt::Vertical);
trimWidget->setIndices(getTrimSliderType(type), getTrimButtonType(type, 0), getTrimButtonType(type, 1)); trimWidget->setIndices(getTrimSliderType(type), getTrimButtonType(type, 0), getTrimButtonType(type, 1));
connect(trimWidget, &RadioTrimWidget::trimButtonPressed, this, &VirtualJoystickWidget::trimButtonPressed); connect(trimWidget, &RadioTrimWidget::valueChange, this, &VirtualJoystickWidget::valueChange);
connect(trimWidget, &RadioTrimWidget::trimButtonReleased, this, &VirtualJoystickWidget::trimButtonReleased);
connect(trimWidget, &RadioTrimWidget::trimSliderMoved, this, &VirtualJoystickWidget::trimSliderMoved);
return trimWidget; return trimWidget;
} }
@ -371,6 +455,24 @@ QLayout *VirtualJoystickWidget::createNodeValueLayout(QChar type, QLabel *& valL
return layout; return layout;
} }
int VirtualJoystickWidget::getStickIndex(QChar type)
{
using namespace Board;
if (stickSide == 'L') {
if (type == 'H')
return STICK_AXIS_LH;
else
return STICK_AXIS_LV;
}
else {
if (type == 'H')
return STICK_AXIS_RH;
else
return STICK_AXIS_RV;
}
}
int VirtualJoystickWidget::getTrimSliderType(QChar type) int VirtualJoystickWidget::getTrimSliderType(QChar type)
{ {
using namespace Board; using namespace Board;
@ -426,10 +528,12 @@ int VirtualJoystickWidget::getTrimButtonType(QChar type, int pos)
RadioTrimWidget * VirtualJoystickWidget::getTrimWidget(int which) RadioTrimWidget * VirtualJoystickWidget::getTrimWidget(int which)
{ {
if (which == Board::TRIM_AXIS_LH || which == Board::TRIM_AXIS_RH) if ((stickSide == 'L' && which == Board::TRIM_AXIS_LH) || (stickSide == 'R' && which == Board::TRIM_AXIS_RH))
return hTrimWidget; return hTrimWidget;
else else if ((stickSide == 'L' && which == Board::TRIM_AXIS_LV) || (stickSide == 'R' && which == Board::TRIM_AXIS_RV))
return vTrimWidget; return vTrimWidget;
else
return NULL;
} }
void VirtualJoystickWidget::onButtonChange(bool checked) void VirtualJoystickWidget::onButtonChange(bool checked)
@ -456,9 +560,9 @@ void VirtualJoystickWidget::onButtonChange(bool checked)
void VirtualJoystickWidget::updateNodeValueLabels() void VirtualJoystickWidget::updateNodeValueLabels()
{ {
if (nodeLabelX) if (nodeLabelX)
nodeLabelX->setText(QString("%1").arg((qreal)node->getX() * 100 + getTrimValue(0) / 5, 2, 'f', 0)); nodeLabelX->setText(QString("%1").arg(node->getX() * 100.0f, 2, 'f', 0));
if (nodeLabelY) if (nodeLabelY)
nodeLabelY->setText(QString("%1").arg((qreal)node->getY() * -100 + getTrimValue(1) / 5, 2, 'f', 0)); nodeLabelY->setText(QString("%1").arg(node->getY() * -100.0f, 2, 'f', 0));
} }
void VirtualJoystickWidget::onGsMouseEvent(QGraphicsSceneMouseEvent * event) void VirtualJoystickWidget::onGsMouseEvent(QGraphicsSceneMouseEvent * event)

View file

@ -21,6 +21,8 @@
#ifndef VIRTUALJOYSTICKWIDGET_H #ifndef VIRTUALJOYSTICKWIDGET_H
#define VIRTUALJOYSTICKWIDGET_H #define VIRTUALJOYSTICKWIDGET_H
#include "radiowidget.h"
#include <QWidget> #include <QWidget>
#include <QResizeEvent> #include <QResizeEvent>
#include <QBoxLayout> #include <QBoxLayout>
@ -28,6 +30,7 @@
#include <QGraphicsView> #include <QGraphicsView>
#include <QGraphicsScene> #include <QGraphicsScene>
#include <QToolButton> #include <QToolButton>
#include <QTimer>
#include <QLabel> #include <QLabel>
class CustomGraphicsScene; class CustomGraphicsScene;
@ -40,38 +43,47 @@ class VirtualJoystickWidget : public QWidget
public: public:
enum ConstraintTypes { enum ConstraintTypes {
HOLD_X = 0, HOLD_X = 0x01,
HOLD_Y, HOLD_Y = 0x02,
FIX_X, FIX_X = 0x04,
FIX_Y FIX_Y = 0x08
}; };
explicit VirtualJoystickWidget(QWidget * parent = NULL, QChar side = 'L', bool showTrims = true, bool showBtns = true, bool showValues = true, QSize size = QSize(125, 125)); explicit VirtualJoystickWidget(QWidget * parent = NULL, QChar side = 'L', bool showTrims = true, bool showBtns = true, bool showValues = true, QSize size = QSize(125, 125));
void setStickX(qreal x);
void setStickY(qreal y);
void setStickPos(QPointF xy);
void centerStick();
qreal getStickX(); qreal getStickX();
qreal getStickY(); qreal getStickY();
QPointF getStickPos(); QPointF getStickPos();
void setTrimValue(int which, int value);
void setTrimRange(int which, int min, int max);
int getTrimValue(int which); int getTrimValue(int which);
void setStickConstraint(int which, bool active);
void setStickColor(const QColor & color);
virtual QSize sizeHint() const; virtual QSize sizeHint() const;
virtual void resizeEvent(QResizeEvent *event); virtual void resizeEvent(QResizeEvent *event);
int getStickScale() const;
void setStickScale(int stickScale);
public slots:
void setStickX(qreal x);
void setStickY(qreal y);
void setStickPos(QPointF xy);
void setStickAxisValue(int index, int value);
void centerStick();
void setTrimsRange(int min, int max);
void setTrimsRange(int rng);
void setTrimRange(int index, int min, int max);
void setTrimValue(int index, int value);
void setWidgetValue(const RadioWidget::RadioWidgetType type, const int index, const int value);
void setStickIndices(int xIdx = -1, int yIdx = -1);
void setStickConstraint(quint8 index, bool active);
void setStickColor(const QColor & color);
void loadDefaultsForMode(const unsigned mode);
signals: signals:
void trimButtonPressed(int index); void valueChange(const RadioWidget::RadioWidgetType type, const int index, int value);
void trimButtonReleased(int index);
void trimSliderMoved(int index, int value);
protected slots: protected slots:
void onNodeXChanged();
void onNodeYChanged();
void onButtonChange(bool checked); void onButtonChange(bool checked);
void updateNodeValueLabels(); void updateNodeValueLabels();
void onGsMouseEvent(QGraphicsSceneMouseEvent * event); void onGsMouseEvent(QGraphicsSceneMouseEvent * event);
@ -81,6 +93,7 @@ class VirtualJoystickWidget : public QWidget
RadioTrimWidget * createTrimWidget(QChar type); RadioTrimWidget * createTrimWidget(QChar type);
QToolButton * createButtonWidget(int type); QToolButton * createButtonWidget(int type);
QLayout * createNodeValueLayout(QChar type, QLabel *& valLabel); QLayout * createNodeValueLayout(QChar type, QLabel *& valLabel);
int getStickIndex(QChar type);
int getTrimSliderType(QChar type); int getTrimSliderType(QChar type);
int getTrimButtonType(QChar type, int pos); int getTrimButtonType(QChar type, int pos);
RadioTrimWidget * getTrimWidget(int which); RadioTrimWidget * getTrimWidget(int which);
@ -97,7 +110,11 @@ class VirtualJoystickWidget : public QWidget
QToolButton * btnFixX, * btnFixY; QToolButton * btnFixX, * btnFixY;
QLabel * nodeLabelX, * nodeLabelY; QLabel * nodeLabelX, * nodeLabelY;
QSize extraSize; QSize extraSize;
QTimer centerStickTimer;
float ar; // aspect ratio float ar; // aspect ratio
int m_xIndex;
int m_yIndex;
int m_stickScale;
bool m_stickPressed; bool m_stickPressed;
}; };

View file

@ -442,7 +442,7 @@ void evalInputs(uint8_t mode)
if (v < -RESX) v = -RESX; if (v < -RESX) v = -RESX;
if (v > RESX) v = RESX; if (v > RESX) v = RESX;
#if defined(PCBTARANIS) && !defined(PCBX7) #if defined(PCBTARANIS) && !defined(PCBX7) && !defined(SIMU)
// TODO why not in the driver? // TODO why not in the driver?
if (i==POT1 || i==SLIDER1) { if (i==POT1 || i==SLIDER1) {
v = -v; v = -v;

View file

@ -68,7 +68,6 @@ uint32_t readTrims()
result |= 0x40; result |= 0x40;
if (~TRIMS_GPIO_REG_RHR & TRIMS_GPIO_PIN_RHR) if (~TRIMS_GPIO_REG_RHR & TRIMS_GPIO_PIN_RHR)
result |= 0x80; result |= 0x80;
#if !defined(SIMU)
if (~TRIMS_GPIO_REG_LSD & TRIMS_GPIO_PIN_LSD) if (~TRIMS_GPIO_REG_LSD & TRIMS_GPIO_PIN_LSD)
result |= 0x100; result |= 0x100;
if (~TRIMS_GPIO_REG_LSU & TRIMS_GPIO_PIN_LSU) if (~TRIMS_GPIO_REG_LSU & TRIMS_GPIO_PIN_LSU)
@ -77,7 +76,6 @@ uint32_t readTrims()
result |= 0x400; result |= 0x400;
if (~TRIMS_GPIO_REG_RSU & TRIMS_GPIO_PIN_RSU) if (~TRIMS_GPIO_REG_RSU & TRIMS_GPIO_PIN_RSU)
result |= 0x800; result |= 0x800;
#endif
// TRACE("readTrims(): result=0x%04x", result); // TRACE("readTrims(): result=0x%04x", result);
return result; return result;

View file

@ -26,7 +26,9 @@ if(Qt5Widgets_FOUND)
set(SIMULATOR_TARGET ${SIMULATOR_FLAVOUR}-simulator) set(SIMULATOR_TARGET ${SIMULATOR_FLAVOUR}-simulator)
add_definitions(-DSIMULATOR_FLAVOUR="${SIMULATOR_FLAVOUR}") add_definitions(-DSIMULATOR_FLAVOUR="${SIMULATOR_FLAVOUR}")
include_directories(${COMPANION_SRC_DIRECTORY} ${COMPANION_SRC_DIRECTORY}/simulation) include_directories(${COMPANION_SRC_DIRECTORY} ${COMPANION_SRC_DIRECTORY}/simulation)
add_library(${SIMULATOR_TARGET} SHARED ${SIMU_SRC} opentxsimulator.cpp) qt5_wrap_cpp(SIMULATOR_SRC ${COMPANION_SRC_DIRECTORY}/simulation/simulatorinterface.h opentxsimulator.h)
list(APPEND SIMULATOR_SRC ${SIMU_SRC} opentxsimulator.cpp)
add_library(${SIMULATOR_TARGET} SHARED ${SIMULATOR_SRC})
add_dependencies(${SIMULATOR_TARGET} ${FIRMWARE_DEPENDENCIES}) add_dependencies(${SIMULATOR_TARGET} ${FIRMWARE_DEPENDENCIES})
target_compile_definitions(${SIMULATOR_TARGET} PUBLIC ${APP_COMMON_DEFINES}) # set in top-level CMakeLists target_compile_definitions(${SIMULATOR_TARGET} PUBLIC ${APP_COMMON_DEFINES}) # set in top-level CMakeLists
target_link_libraries(${SIMULATOR_TARGET} ${SDL_LIBRARY}) target_link_libraries(${SIMULATOR_TARGET} ${SDL_LIBRARY})

View file

@ -18,7 +18,23 @@
#include "opentx.h" #include "opentx.h"
#include "simulcd.h" #include "simulcd.h"
#include <QDebug>
#include <QElapsedTimer>
#if !defined(MAX_LOGICAL_SWITCHES) && defined(NUM_CSW)
#define MAX_LOGICAL_SWITCHES NUM_CSW
#endif
#if defined(CPUARM)
#define GET_SWITCH_BOOL(sw__) getSwitch((sw__), 0);
#else
#define GET_SWITCH_BOOL(sw__) getSwitch(sw__);
#endif
#define OTXS_DBG qDebug() << "(" << simuTimerMicros() << "us)"
int16_t g_anas[Analogs::NUM_ANALOGS]; int16_t g_anas[Analogs::NUM_ANALOGS];
QVector<QIODevice *> OpenTxSimulator::tracebackDevices;
uint16_t anaIn(uint8_t chan) uint16_t anaIn(uint8_t chan)
{ {
@ -30,36 +46,135 @@ uint16_t getAnalogValue(uint8_t index)
return anaIn(index); return anaIn(index);
} }
bool hasExtendedTrims() void firmwareTraceCb(const char * text)
{ {
return g_model.extendedTrims; foreach (QIODevice * dev, OpenTxSimulator::tracebackDevices) {
if (dev)
dev->write(text);
}
} }
uint8_t getStickMode() OpenTxSimulator::OpenTxSimulator() :
SimulatorInterface(),
m_timer10ms(NULL),
m_resetOutputsData(true),
m_stopRequested(false)
{ {
return limit<uint8_t>(0, g_eeGeneral.stickMode, 3); tracebackDevices.clear();
traceCallback = firmwareTraceCb;
} }
OpenTxSimulator::~OpenTxSimulator()
OpenTxSimulator::OpenTxSimulator()
{ {
traceCallback = NULL;
tracebackDevices.clear();
if (m_timer10ms)
delete m_timer10ms;
if (isRunning()) {
stop();
QElapsedTimer tmout;
tmout.start();
while (isRunning() && !tmout.hasExpired(1000))
;
}
//qDebug() << "Deleting OpenTxSimulator";
}
QString OpenTxSimulator::name()
{
return QString(SIMULATOR_FLAVOUR);
}
bool OpenTxSimulator::isRunning()
{
QMutexLocker lckr(&m_mtxSimuMain);
return (bool)main_thread_running;
}
void OpenTxSimulator::init()
{
if (isRunning())
return;
OTXS_DBG;
if (!m_timer10ms) {
// make sure we create & control the timer from current thread
m_timer10ms = new QTimer();
m_timer10ms->setInterval(10);
connect(m_timer10ms, &QTimer::timeout, this, &OpenTxSimulator::run);
connect(this, SIGNAL(started()), m_timer10ms, SLOT(start()));
connect(this, SIGNAL(stopped()), m_timer10ms, SLOT(stop()));
}
m_resetOutputsData = true;
setStopRequested(false);
QMutexLocker lckr(&m_mtxSimuMain);
memset(g_anas, 0, sizeof(g_anas));
simuInit();
}
void OpenTxSimulator::start(const char * filename, bool tests)
{
if (isRunning())
return;
OTXS_DBG << "file:" << filename << "tests:" << tests;
QMutexLocker lckr(&m_mtxSimuMain);
QMutexLocker slckr(&m_mtxSettings);
StartEepromThread(filename);
StartAudioThread(volumeGain);
StartSimu(tests, simuSdDirectory.toLatin1().constData(), simuSettingsDirectory.toLatin1().constData());
emit started();
QTimer::singleShot(0, this, SLOT(run())); // old style for Qt < 5.4
}
void OpenTxSimulator::stop()
{
if (!isRunning())
return;
OTXS_DBG;
setStopRequested(true);
QMutexLocker lckr(&m_mtxSimuMain);
StopSimu();
StopAudioThread();
StopEepromThread();
emit stopped();
} }
void OpenTxSimulator::setSdPath(const QString & sdPath, const QString & settingsPath) void OpenTxSimulator::setSdPath(const QString & sdPath, const QString & settingsPath)
{ {
QMutexLocker lckr(&m_mtxSettings);
simuSdDirectory = sdPath; simuSdDirectory = sdPath;
simuSettingsDirectory = settingsPath; simuSettingsDirectory = settingsPath;
} }
void OpenTxSimulator::setVolumeGain(int value) void OpenTxSimulator::setVolumeGain(const int value)
{ {
QMutexLocker lckr(&m_mtxSettings);
volumeGain = value; volumeGain = value;
} }
bool OpenTxSimulator::timer10ms() void OpenTxSimulator::setRadioData(const QByteArray & data)
{ {
#define TIMER10MS_IMPORT #if defined(EEPROM_SIZE)
#include "simulatorimport.h" QMutexLocker lckr(&m_mtxRadioData);
memcpy(eeprom, data.data(), qMin<int>(EEPROM_SIZE, data.size()));
#endif
}
void OpenTxSimulator::readRadioData(QByteArray & dest)
{
#if defined(EEPROM_SIZE)
QMutexLocker lckr(&m_mtxRadioData);
memcpy(dest.data(), eeprom, std::min<int>(EEPROM_SIZE, dest.size()));
#endif
} }
uint8_t * OpenTxSimulator::getLcd() uint8_t * OpenTxSimulator::getLcd()
@ -67,126 +182,139 @@ uint8_t * OpenTxSimulator::getLcd()
return (uint8_t *)simuLcdBuf; return (uint8_t *)simuLcdBuf;
} }
bool OpenTxSimulator::lcdChanged(bool & lightEnable) void OpenTxSimulator::setAnalogValue(uint8_t index, int16_t value)
{ {
#define LCDCHANGED_IMPORT static int dim = DIM(g_anas);
#include "simulatorimport.h" if (index < dim)
g_anas[index] = value;
} }
void OpenTxSimulator::start(QByteArray & ee, bool tests) void OpenTxSimulator::setSwitch(uint8_t swtch, int8_t state)
{ {
#if defined(EEPROM_SIZE) simuSetSwitch(swtch, state);
memcpy(eeprom, ee.data(), std::min<int>(EEPROM_SIZE, ee.size()));
#endif
start((const char *)0, tests);
} }
void OpenTxSimulator::start(const char * filename, bool tests) void OpenTxSimulator::setKey(uint8_t key, bool state)
{ {
if (main_thread_running) simuSetKey(key, state);
return;
StartEepromThread(filename);
StartAudioThread(volumeGain);
StartSimu(tests, simuSdDirectory.toLatin1().constData(), simuSettingsDirectory.toLatin1().constData());
} }
void OpenTxSimulator::stop() void OpenTxSimulator::setTrimSwitch(uint8_t trim, bool state)
{ {
if (!main_thread_running) simuSetTrim(trim, state);
return;
StopSimu();
#if defined(CPUARM)
StopAudioThread();
#endif
StopEepromThread();
}
void OpenTxSimulator::readEepromData(QByteArray & dest)
{
#if defined(EEPROM_SIZE)
memcpy(dest.data(), eeprom, std::min<int>(EEPROM_SIZE, dest.size()));
#endif
}
void OpenTxSimulator::getValues(TxOutputs &outputs)
{
#define GETVALUES_IMPORT
#define g_chans512 channelOutputs
#include "simulatorimport.h"
}
void OpenTxSimulator::setValues(TxInputs &inputs)
{
#define SETVALUES_IMPORT
#include "simulatorimport.h"
} }
void OpenTxSimulator::setTrim(unsigned int idx, int value) void OpenTxSimulator::setTrim(unsigned int idx, int value)
{ {
idx = modn12x3[4*getStickMode() + idx]; unsigned i = idx;
uint8_t phase = getTrimFlightMode(getFlightMode(), idx); if (i < 4) // swap axes
setTrimValue(phase, idx, value); i = modn12x3[4 * getStickMode() + idx];
uint8_t phase = getTrimFlightMode(getFlightMode(), i);
if (!setTrimValue(phase, i, value)) {
QTimer *timer = new QTimer(this);
timer->setSingleShot(true);
connect(timer, &QTimer::timeout, [=]() {
emit trimValueChange(idx, 0);
emit outputValueChange(OUTPUT_SRC_TRIM_VALUE, idx, 0);
timer->deleteLater();
});
timer->start(350);
}
} }
void OpenTxSimulator::getTrims(Trims & trims) void OpenTxSimulator::setTrainerInput(unsigned int inputNumber, int16_t value)
{ {
uint8_t phase = getFlightMode(); static unsigned dim = DIM(ppmInput);
trims.extended = hasExtendedTrims(); //setTrainerTimeout(100);
for (uint8_t idx=0; idx < CPN_MAX_TRIMS; idx++) { if (inputNumber < dim)
trims.values[idx] = getTrimValue(getTrimFlightMode(phase, idx), idx); ppmInput[inputNumber] = qMin(qMax((int16_t)-512, value), (int16_t)512);
} }
for (int i=0; i<2; i++) { void OpenTxSimulator::setInputValue(int type, uint8_t index, int16_t value)
uint8_t idx = modn12x3[4*getStickMode() + i]; {
int16_t tmp = trims.values[i]; //qDebug() << type << index << value << this->thread();
trims.values[i] = trims.values[idx]; switch (type) {
trims.values[idx] = tmp; case INPUT_SRC_ANALOG :
case INPUT_SRC_STICK :
setAnalogValue(index, value);
break;
case INPUT_SRC_KNOB :
setAnalogValue(index + NUM_STICKS, value);
break;
case INPUT_SRC_SLIDER :
setAnalogValue(index + NUM_STICKS + NUM_POTS, value);
break;
case INPUT_SRC_TXVIN :
setAnalogValue(Analogs::TX_VOLTAGE, voltageToAdc(value));
break;
case INPUT_SRC_SWITCH :
setSwitch(index, (int8_t)value);
break;
case INPUT_SRC_TRIM_SW :
setTrimSwitch(index, (bool)value);
break;
case INPUT_SRC_TRIM :
setTrim(index, value);
break;
case INPUT_SRC_KEY :
setKey(index, (bool)value);
break;
case INPUT_SRC_TRAINER :
setTrainerInput(index, value);
break;
case INPUT_SRC_ROTENC : // TODO
default:
return;
} }
} }
void OpenTxSimulator::wheelEvent(int steps) void OpenTxSimulator::rotaryEncoderEvent(int steps)
{ {
#if defined(ROTARY_ENCODER_NAVIGATION) #if defined(ROTARY_ENCODER_NAVIGATION)
ROTARY_ENCODER_NAVIGATION_VALUE += steps * ROTARY_ENCODER_GRANULARITY; ROTARY_ENCODER_NAVIGATION_VALUE += steps * ROTARY_ENCODER_GRANULARITY;
#else #else
// TODO : this should probably be handled in the GUI
int key;
if (steps > 0) if (steps > 0)
simuSetKey(KEY_MINUS, 1); key = KEY_MINUS;
else if (steps < 0) else if (steps < 0)
simuSetKey(KEY_PLUS, 1); key = KEY_PLUS;
setKey(key, 1);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
QTimer::singleShot(10, [this, key]() { setKey(key, 0); });
#else
QTimer *timer = new QTimer(this);
timer->setSingleShot(true);
connect(timer, &QTimer::timeout, [=]() {
setKey(key, 0);
timer->deleteLater();
} );
timer->start(10);
#endif #endif
#endif // defined(ROTARY_ENCODER_NAVIGATION)
} }
unsigned int OpenTxSimulator::getPhase() void OpenTxSimulator::setTrainerTimeout(uint16_t ms)
{ {
return getFlightMode(); ppmInputValidityTimer = ms;
}
const char * OpenTxSimulator::getPhaseName(unsigned int phase)
{
static char buff[sizeof(g_model.flightModeData[0].name)+1];
zchar2str(buff, g_model.flightModeData[phase].name, sizeof(g_model.flightModeData[0].name));
return buff;
}
const char * OpenTxSimulator::getError()
{
#define GETERROR_IMPORT
#include "simulatorimport.h"
} }
void OpenTxSimulator::sendTelemetry(uint8_t * data, unsigned int len) void OpenTxSimulator::sendTelemetry(uint8_t * data, unsigned int len)
{ {
Q_UNUSED(len)
#if defined(TELEMETRY_FRSKY_SPORT) #if defined(TELEMETRY_FRSKY_SPORT)
sportProcessTelemetryPacket(data); sportProcessTelemetryPacket(data);
#else
Q_UNUSED(data)
#endif #endif
} }
uint8_t OpenTxSimulator::getSensorInstance(uint16_t id, uint8_t defaultValue) uint8_t OpenTxSimulator::getSensorInstance(uint16_t id, uint8_t defaultValue)
{ {
#if defined(TELEMETRY_FRSKY_SPORT) #if defined(TELEMETRY_FRSKY_SPORT)
for (int i = 0; i<MAX_TELEMETRY_SENSORS; i++) { for (int i = 0; i < MAX_TELEMETRY_SENSORS; i++) {
if (isTelemetryFieldAvailable(i)) { if (isTelemetryFieldAvailable(i)) {
TelemetrySensor * sensor = &g_model.telemetrySensors[i]; TelemetrySensor * sensor = &g_model.telemetrySensors[i];
if (sensor->id == id) { if (sensor->id == id) {
@ -194,6 +322,8 @@ uint8_t OpenTxSimulator::getSensorInstance(uint16_t id, uint8_t defaultValue)
} }
} }
} }
#else
Q_UNUSED(id)
#endif #endif
return defaultValue; return defaultValue;
} }
@ -201,7 +331,7 @@ uint8_t OpenTxSimulator::getSensorInstance(uint16_t id, uint8_t defaultValue)
uint16_t OpenTxSimulator::getSensorRatio(uint16_t id) uint16_t OpenTxSimulator::getSensorRatio(uint16_t id)
{ {
#if defined(TELEMETRY_FRSKY_SPORT) #if defined(TELEMETRY_FRSKY_SPORT)
for (int i = 0; i<MAX_TELEMETRY_SENSORS; i++) { for (int i = 0; i < MAX_TELEMETRY_SENSORS; i++) {
if (isTelemetryFieldAvailable(i)) { if (isTelemetryFieldAvailable(i)) {
TelemetrySensor * sensor = &g_model.telemetrySensors[i]; TelemetrySensor * sensor = &g_model.telemetrySensors[i];
if (sensor->id == id) { if (sensor->id == id) {
@ -209,14 +339,41 @@ uint16_t OpenTxSimulator::getSensorRatio(uint16_t id)
} }
} }
} }
#else
Q_UNUSED(id)
#endif #endif
return 0; return 0;
} }
void OpenTxSimulator::setTrainerInput(unsigned int inputNumber, int16_t value) const int OpenTxSimulator::getCapability(Capability cap)
{ {
#define SETTRAINER_IMPORT int ret = 0;
#include "simulatorimport.h" switch(cap) {
case CAP_LUA :
#ifdef LUA
ret = 1;
#endif
break;
case CAP_ROTARY_ENC :
#ifdef ROTARY_ENCODERS
ret = ROTARY_ENCODERS;
#endif
break;
case CAP_ROTARY_ENC_NAV :
#ifdef ROTARY_ENCODER_NAVIGATION
ret = 1;
#endif
break;
case CAP_TELEM_FRSKY_SPORT :
#ifdef TELEMETRY_FRSKY_SPORT
ret = 1;
#endif
break;
}
return ret;
} }
void OpenTxSimulator::setLuaStateReloadPermanentScripts() void OpenTxSimulator::setLuaStateReloadPermanentScripts()
@ -226,11 +383,206 @@ void OpenTxSimulator::setLuaStateReloadPermanentScripts()
#endif #endif
} }
void OpenTxSimulator::installTraceHook(void (*callback)(const char *)) void OpenTxSimulator::addTracebackDevice(QIODevice * device)
{ {
traceCallback = callback; QMutexLocker lckr(&m_mtxTbDevices);
if (device && !tracebackDevices.contains(device))
tracebackDevices.append(device);
} }
void OpenTxSimulator::removeTracebackDevice(QIODevice * device)
{
if (device) {
QMutexLocker lckr(&m_mtxTbDevices);
// no QVector::removeAll() in Qt < 5.4
int i = 0;
foreach (QIODevice * d, tracebackDevices) {
if (d == device)
tracebackDevices.remove(i);
++i;
}
}
}
/*** Protected classes ***/
void OpenTxSimulator::run()
{
static uint32_t loops = 0;
static QElapsedTimer ts;
if (!loops)
ts.start();
if (isStopRequested()) {
return;
}
if (!isRunning()) {
QString err(getError());
emit runtimeError(err);
emit stopped();
return;
}
++loops;
per10ms();
checkLcdChanged();
if (!(loops % 5)) {
checkOutputsChanged();
}
if (!(loops % (SIMULATOR_INTERFACE_HEARTBEAT_PERIOD / 10))) {
emit heartbeat(loops, simuTimerMicros() / 1000);
}
}
bool OpenTxSimulator::isStopRequested()
{
QMutexLocker lckr(&m_mtxStopReq);
return m_stopRequested;
}
void OpenTxSimulator::setStopRequested(bool stop)
{
QMutexLocker lckr(&m_mtxStopReq);
m_stopRequested = stop;
}
bool OpenTxSimulator::checkLcdChanged()
{
if (simuLcdRefresh) {
simuLcdRefresh = false;
emit lcdChange(isBacklightEnabled());
return true;
}
return false;
}
void OpenTxSimulator::checkOutputsChanged()
{
static TxOutputs lastOutputs;
static size_t chansDim = DIM(channelOutputs);
qint32 tmpVal;
uint8_t i, idx;
uint8_t phase = getFlightMode(); // opentx.cpp
uint8_t mode = getStickMode();
for (i=0; i < chansDim; i++) {
if (lastOutputs.chans[i] != channelOutputs[i] || m_resetOutputsData) {
emit channelOutValueChange(i, channelOutputs[i]);
emit channelMixValueChange(i, ex_chans[i]);
emit outputValueChange(OUTPUT_SRC_CHAN_OUT, i, channelOutputs[i]);
emit outputValueChange(OUTPUT_SRC_CHAN_MIX, i, ex_chans[i]);
lastOutputs.chans[i] = channelOutputs[i];
}
}
for (i=0; i < MAX_LOGICAL_SWITCHES; i++) {
tmpVal = (qint32)GET_SWITCH_BOOL(SWSRC_SW1+i);
if (lastOutputs.vsw[i] != (bool)tmpVal || m_resetOutputsData) {
emit virtualSwValueChange(i, tmpVal);
emit outputValueChange(OUTPUT_SRC_VIRTUAL_SW, i, tmpVal);
lastOutputs.vsw[i] = tmpVal;
}
}
for (i=0; i < Board::TRIM_AXIS_COUNT; i++) {
if (i < 4) // swap axes
idx = modn12x3[4 * mode + i];
else
idx = i;
tmpVal = getTrimValue(getTrimFlightMode(phase, idx), idx);
if (lastOutputs.trims[i] != tmpVal || m_resetOutputsData) {
emit trimValueChange(i, tmpVal);
emit outputValueChange(OUTPUT_SRC_TRIM_VALUE, i, tmpVal);
lastOutputs.trims[i] = tmpVal;
}
}
tmpVal = g_model.extendedTrims ? TRIM_EXTENDED_MAX : TRIM_MAX;
if (lastOutputs.trimRange != tmpVal || m_resetOutputsData) {
emit trimRangeChange(Board::TRIM_AXIS_COUNT, -tmpVal, tmpVal);
emit outputValueChange(OUTPUT_SRC_TRIM_RANGE, Board::TRIM_AXIS_COUNT, tmpVal);
lastOutputs.trimRange = tmpVal;
}
if (lastOutputs.phase != phase || m_resetOutputsData) {
emit phaseChanged(phase, getCurrentPhaseName());
emit outputValueChange(OUTPUT_SRC_PHASE, 0, qint16(phase));
lastOutputs.phase = phase;
}
#if defined(GVAR_VALUE) && defined(GVARS)
gVarMode_t gvar;
for (uint8_t fm=0; fm < MAX_FLIGHT_MODES; fm++) {
gvar.mode = fm;
for (uint8_t gv=0; gv < MAX_GVARS; gv++) {
tmpVal = GVAR_VALUE(gv, getGVarFlightMode(fm, gv));
if (lastOutputs.gvars[fm][gv] != tmpVal || m_resetOutputsData) {
lastOutputs.gvars[fm][gv] = tmpVal;
gvar.value = (int16_t)tmpVal;
tmpVal = gvar;
emit gVarValueChange(gv, tmpVal);
emit outputValueChange(OUTPUT_SRC_GVAR, gv, tmpVal);
}
}
}
#endif
m_resetOutputsData = false;
}
uint8_t OpenTxSimulator::getStickMode()
{
return limit<uint8_t>(0, g_eeGeneral.stickMode, 3);
}
const char * OpenTxSimulator::getPhaseName(unsigned int phase)
{
static char buff[sizeof(g_model.flightModeData[0].name)+1];
zchar2str(buff, g_model.flightModeData[phase].name, sizeof(g_model.flightModeData[0].name));
return buff;
}
const QString OpenTxSimulator::getCurrentPhaseName()
{
unsigned phase = getFlightMode();
QString name(getPhaseName(phase));
if (name.isEmpty())
name = QString::number(phase);
return name;
}
const char * OpenTxSimulator::getError()
{
return main_thread_error;
}
const int OpenTxSimulator::voltageToAdc(const int volts)
{
int ret = 0;
#if defined(PCBHORUS) || defined(PCBX7)
ret = (float)volts * 16.2f;
#elif defined(PCBTARANIS) || defined(PCBFLAMENCO) || defined(PCBSKY9X)
ret = (float)volts * 13.3f;
#elif defined(PCBGRUVIN9X)
ret = (float)volts * 1.63f;
#else
ret = (float)volts * 14.15f;
#endif
return ret;
}
/*
* OpenTxSimulatorFactory
*/
class OpenTxSimulatorFactory: public SimulatorFactory class OpenTxSimulatorFactory: public SimulatorFactory
{ {
public: public:

View file

@ -19,68 +19,84 @@
#include "simulatorinterface.h" #include "simulatorinterface.h"
#include <QMutex>
#include <QObject>
#include <QTimer>
#if defined __GNUC__ #if defined __GNUC__
#define DLLEXPORT #define DLLEXPORT
#else #else
#define DLLEXPORT __declspec(dllexport) #define DLLEXPORT __declspec(dllexport)
#endif #endif
class DLLEXPORT OpenTxSimulator : public SimulatorInterface { class DLLEXPORT OpenTxSimulator : public SimulatorInterface
{
private: Q_OBJECT
int volumeGain;
QString simuSdDirectory;
QString simuSettingsDirectory;
public: public:
OpenTxSimulator(); OpenTxSimulator();
virtual ~OpenTxSimulator();
virtual void setSdPath(const QString & sdPath = "", const QString & settingsPath = ""); virtual QString name();
virtual bool isRunning();
virtual void setVolumeGain(int value); virtual void readRadioData(QByteArray & dest);
virtual void start(QByteArray & eeprom, bool tests=true);
virtual void start(const char * filename, bool tests=true);
virtual void stop();
virtual void readEepromData(QByteArray & dest);
virtual bool timer10ms();
virtual uint8_t * getLcd(); virtual uint8_t * getLcd();
virtual bool lcdChanged(bool & lightEnable);
virtual void setValues(TxInputs &inputs);
virtual void getValues(TxOutputs &outputs);
virtual void setTrim(unsigned int idx, int value);
virtual void getTrims(Trims & trims);
virtual unsigned int getPhase();
virtual const char * getPhaseName(unsigned int phase);
virtual void wheelEvent(int steps);
virtual const char * getError();
virtual void sendTelemetry(uint8_t * data, unsigned int len);
virtual uint8_t getSensorInstance(uint16_t id, uint8_t defaultValue = 0); virtual uint8_t getSensorInstance(uint16_t id, uint8_t defaultValue = 0);
virtual uint16_t getSensorRatio(uint16_t id); virtual uint16_t getSensorRatio(uint16_t id);
virtual const int getCapability(Capability cap);
static QVector<QIODevice *> tracebackDevices;
public slots:
virtual void init();
virtual void start(const char * filename = NULL, bool tests = true);
virtual void stop();
virtual void setSdPath(const QString & sdPath = "", const QString & settingsPath = "");
virtual void setVolumeGain(const int value);
virtual void setRadioData(const QByteArray & data);
virtual void setAnalogValue(uint8_t index, int16_t value);
virtual void setKey(uint8_t key, bool state);
virtual void setSwitch(uint8_t swtch, int8_t state);
virtual void setTrim(unsigned int idx, int value);
virtual void setTrimSwitch(uint8_t trim, bool state);
virtual void setTrainerInput(unsigned int inputNumber, int16_t value); virtual void setTrainerInput(unsigned int inputNumber, int16_t value);
virtual void setInputValue(int type, uint8_t index, int16_t value);
virtual void installTraceHook(void (*callback)(const char *)); virtual void rotaryEncoderEvent(int steps);
virtual void setTrainerTimeout(uint16_t ms);
virtual void sendTelemetry(uint8_t * data, unsigned int len);
virtual void setLuaStateReloadPermanentScripts(); virtual void setLuaStateReloadPermanentScripts();
virtual void addTracebackDevice(QIODevice * device);
virtual void removeTracebackDevice(QIODevice * device);
protected slots:
void run();
protected:
bool isStopRequested();
void setStopRequested(bool stop);
bool checkLcdChanged();
void checkOutputsChanged();
uint8_t getStickMode();
const char * getPhaseName(unsigned int phase);
const QString getCurrentPhaseName();
const char * getError();
const int voltageToAdc(const int volts);
QString simuSdDirectory;
QString simuSettingsDirectory;
QTimer * m_timer10ms;
QMutex m_mtxStopReq;
QMutex m_mtxSimuMain;
QMutex m_mtxRadioData;
QMutex m_mtxSettings;
QMutex m_mtxTbDevices;
int volumeGain;
bool m_resetOutputsData;
bool m_stopRequested;
}; };
#endif // _OPENTX_SIMULATOR_H_ #endif // _OPENTX_SIMULATOR_H_

View file

@ -257,10 +257,10 @@ void simuSetTrim(uint8_t trim, bool state)
TRIM_CASE(6, TRIMS_GPIO_REG_RHL, TRIMS_GPIO_PIN_RHL) TRIM_CASE(6, TRIMS_GPIO_REG_RHL, TRIMS_GPIO_PIN_RHL)
TRIM_CASE(7, TRIMS_GPIO_REG_RHR, TRIMS_GPIO_PIN_RHR) TRIM_CASE(7, TRIMS_GPIO_REG_RHR, TRIMS_GPIO_PIN_RHR)
#if defined(PCBHORUS) #if defined(PCBHORUS)
TRIM_CASE(8, TRIMS_GPIO_REG_LSU, TRIMS_GPIO_PIN_LSU) TRIM_CASE(8, TRIMS_GPIO_REG_LSD, TRIMS_GPIO_PIN_LSD)
TRIM_CASE(9, TRIMS_GPIO_REG_LSD, TRIMS_GPIO_PIN_RVU) TRIM_CASE(9, TRIMS_GPIO_REG_LSU, TRIMS_GPIO_PIN_LSU)
TRIM_CASE(10, TRIMS_GPIO_REG_RSU, TRIMS_GPIO_PIN_RHL) TRIM_CASE(10, TRIMS_GPIO_REG_RSD, TRIMS_GPIO_PIN_RSD)
TRIM_CASE(11, TRIMS_GPIO_REG_RSD, TRIMS_GPIO_PIN_RHR) TRIM_CASE(11, TRIMS_GPIO_REG_RSU, TRIMS_GPIO_PIN_RSU)
#endif #endif
} }
} }