[Simulator] Further Simulator improvements. (#4282)

* Refactor CompStoreObj to use templates. Clean up some formatting.

* [Simulator] Add SimulatorOptions type to replace individual simulator options; Fix storage handler for custom types; Cleanup some redundant code.

* [Simulator][Joystick] Rework the joystick options/calibration dialog.  Can now enable/disable/select the Joystick from there. Delay start of calibration until Start is pressed (otherwise looks like last calibration didn't work). And generally refactor a lot of it to be smarter.

* Cosmetics.

* [Simulator] Add state handling for RadioWidget class. Split code to cpp file.

* [Simulator] Add SimulatorStartupDialog as its own UI class.

* [Simulator] Multiple changes and new features:
    All Horus data source options are now fully supported with data properly saved after use (SD path, .otx file, or custom data path);
    Loading .otx files now works for all radio types (but not saving data back to .otx);
    Switch and knob/slider states are now saved to per radio profile;
    Joystick configuration/calibration is now available directly from inside Simulator;
    Due to refactoring, all previously saved per-profile simulator settings are now invalid;
    Further consolidated simulator dialog startup/shutdown, now handles temp. paths internally, etc;
    With WIN_USE_CONSOLE defined, Simulator and Companion when started from Windows command line will now use that console for all messages (instead of opening a new one). This allows full debug output to be seen at all stages of execution and after exit.

* [Simulator] Fix build w/out SDL Joystick support.

* [Simulator] Fix bogus throttle warning on simulator start; Add forgotten RadioWidgetState flags to stream handlers; Add debug state saver in SimulatorOptions.
This commit is contained in:
Max Paperno
2017-01-25 16:20:45 +01:00
committed by Bertrand Songis
parent b53ec7392c
commit 6e2474081f
22 changed files with 2869 additions and 1785 deletions
+5 -2
View File
@@ -71,6 +71,9 @@ endif()
if(WIN32)
include_directories(SYSTEM ${WIN_INCLUDE_DIRS})
if(NOT WIN_USE_CONSOLE)
set(WIN_EXECUTABLE_TYPE WIN32) # GUI (WinMain) app
endif()
if(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc /LD /MP")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS,5.01")
@@ -246,7 +249,7 @@ add_custom_target(gen_qrc DEPENDS ${companion_RCC})
add_definitions(-DQT_TRANSLATIONS_DIR="${QT_TRANSLATIONS_DIR}")
add_executable(${COMPANION_NAME} MACOSX_BUNDLE WIN32 ${companion_SRCS} ${companion_RCC} ${companion_QM})
add_executable(${COMPANION_NAME} MACOSX_BUNDLE ${WIN_EXECUTABLE_TYPE} ${companion_SRCS} ${companion_RCC} ${companion_QM})
add_dependencies(${COMPANION_NAME} gen_qrc)
qt5_use_modules(${COMPANION_NAME} Core Widgets Network)
@@ -275,7 +278,7 @@ if(WIN32)
list(APPEND simu_SRCS icon.rc)
endif()
add_executable(${SIMULATOR_NAME} MACOSX_BUNDLE WIN32 ${simu_SRCS} ${companion_RCC})
add_executable(${SIMULATOR_NAME} MACOSX_BUNDLE ${WIN_EXECUTABLE_TYPE} ${simu_SRCS} ${companion_RCC})
add_dependencies(${SIMULATOR_NAME} gen_qrc)
qt5_use_modules(${SIMULATOR_NAME} Core Widgets Multimedia)
+18 -27
View File
@@ -816,18 +816,17 @@ void startSimulation(QWidget * parent, RadioData & radioData, int modelIdx)
SimulatorInterface * simulator = getCurrentSimulator();
if (simulator) {
#if defined(WIN32) && defined(WIN_USE_CONSOLE_STDIO)
AllocConsole();
SetConsoleTitle("Companion Console");
freopen("conin$", "r", stdin);
freopen("conout$", "w", stdout);
freopen("conout$", "w", stderr);
bool freeConsoleOnClose = false;
if (!GetConsoleWindow()) {
AllocConsole();
SetConsoleTitle("Companion Console");
freeConsoleOnClose = true;
freopen("conin$", "r", stdin);
freopen("conout$", "w", stdout);
freopen("conout$", "w", stderr);
}
#endif
Firmware * firmware = getCurrentFirmware();
RadioData * simuData = new RadioData(radioData);
Board::Type board = getCurrentBoard();
QByteArray eeprom = QByteArray();
QString sdPath = g.profile[g.id()].sdPath();
QString settingsPath = "";
unsigned int flags = 0;
if (modelIdx >= 0) {
@@ -835,29 +834,21 @@ void startSimulation(QWidget * parent, RadioData & radioData, int modelIdx)
simuData->setCurrentModel(modelIdx);
}
if (IS_HORUS(board)) {
QTemporaryDir tmpDir(QDir::tempPath() + "/otx-XXXXXX");
settingsPath = tmpDir.path();
tmpDir.setAutoRemove(false);
}
qDebug() << "Starting" << firmware->getName() << "simulation with SD path" << sdPath << "and models/settings path" << settingsPath;
SimulatorDialog * dialog = new SimulatorDialog(parent, simulator, flags);
dialog->setPaths(sdPath, settingsPath);
if (IS_HORUS(getCurrentBoard()) && !dialog->useTempDataPath(true)) {
QMessageBox::critical(NULL, QObject::tr("Data Load Error"), QObject::tr("Error: Could not create temporary directory in '%1'").arg(QDir::tempPath()));
return;
}
dialog->setRadioData(simuData);
qDebug() << __FILE__ << __LINE__ << "Starting" << getCurrentFirmware()->getName() << "simulation with SD path" << dialog->getSdPath() << "and models/settings path" << dialog->getDataPath();
dialog->start();
dialog->exec();
dialog->deleteLater();
if (!settingsPath.isEmpty()) {
// TODO either delete tmp directory and its contents OR use it to get back data from the simulation
qDebug() << "Simulation finished, deleting temporary settings directory" << settingsPath;
QDir tmp(settingsPath);
tmp.removeRecursively();
}
delete simuData;
// TODO Horus tmp directory is deleted on simulator close OR we could use it to get back data from the simulation
delete simuData; // TODO same with simuData, could read it back and update the file data
#if defined(WIN32) && defined(WIN_USE_CONSOLE_STDIO)
FreeConsole();
if (freeConsoleOnClose)
FreeConsole();
#endif
}
else {
+6 -1
View File
@@ -7,8 +7,10 @@ set(simulation_SRCS
simulateduiwidgetX7.cpp
simulateduiwidgetX9.cpp
simulateduiwidgetX12.cpp
simulatorstartupdialog.cpp
telemetrysimu.cpp
trainersimu.cpp
widgets/radiowidget.cpp
widgets/virtualjoystickwidget.cpp
)
@@ -19,6 +21,7 @@ set(simulation_UIS
simulateduiwidgetX7.ui
simulateduiwidgetX9.ui
simulateduiwidgetX12.ui
simulatorstartupdialog.ui
telemetrysimu.ui
trainersimu.ui
)
@@ -26,8 +29,10 @@ set(simulation_UIS
set(simulation_HDRS
debugoutput.h
radiouiaction.h
simulatordialog.h
simulateduiwidget.h
# simulator.h
simulatordialog.h
simulatorstartupdialog.h
telemetrysimu.h
trainersimu.h
widgets/buttonswidget.h
+183 -85
View File
@@ -20,12 +20,16 @@
#include "joystickdialog.h"
#include "ui_joystickdialog.h"
#include "appdata.h"
joystickDialog::joystickDialog(QWidget *parent, int stick) :
QDialog(parent),
ui(new Ui::joystickDialog) {
QDialog(parent),
ui(new Ui::joystickDialog),
step(0),
numAxes(0),
started(false)
{
ui->setupUi(this);
int jscaltmp[8][3] = {
{32767, 0, -32767},
{32767, 0, -32767},
@@ -37,137 +41,231 @@ ui(new Ui::joystickDialog) {
{32767, 0, -32767}
};
memcpy(jscal, jscaltmp, sizeof (jscal));
ui->okButton->setDisabled(true);
ui->howtoLabel->setText(tr("Move sticks and pots in every direction making full movement\nPress next when finished"));
step = 0;
foreach(QComboBox *cb, findChildren<QComboBox *>(QRegExp("jsmapCB_[0-9]+"))) {
cb->setDisabled(true);
sticks[cb->property("channel").toUInt()] = cb;
}
foreach(QCheckBox *ib, findChildren<QCheckBox *>(QRegExp("ChInv_[0-9]+"))) {
ib->setDisabled(true);
invert[ib->property("channel").toUInt()] = ib;
}
joystickOpen(stick);
foreach(QSlider *sl, findChildren<QSlider *>(QRegExp("Ch_[0-9]+"))) {
sliders[sl->property("channel").toUInt()] = sl;
}
for (int i = 0; i < MAX_JOYSTICKS; ++i) {
if (!g.joystick[i].existsOnDisk())
continue;
jscal[i][0] = g.joystick[i].stick_min();
jscal[i][1] = g.joystick[i].stick_med();
jscal[i][2] = g.joystick[i].stick_max();
sliders[i]->setMinimum(jscal[i][0]);
sliders[i]->setMaximum(jscal[i][2]);
invert[i]->setChecked(g.joystick[i].stick_inv());
sticks[i]->setCurrentIndex(g.joystick[i].stick_axe() + 1);
}
ui->backButton->setEnabled(false);
ui->joystickChkB->setChecked(g.jsSupport() || stick > -1);
if (stick < 0)
stick = g.jsCtrl();
loadJoysticks(stick);
loadStep();
connect(joystick, SIGNAL(axisValueChanged(int, int)), this, SLOT(onjoystickAxisValueChanged(int, int)));
connect(ui->joystickCB, SIGNAL(currentIndexChanged(int)), this, SLOT(joystickOpen(int)));
connect(ui->joystickChkB, SIGNAL(toggled(bool)), this, SLOT(joystickSetEnabled(bool)));
}
joystickDialog::~joystickDialog() {
joystickDialog::~joystickDialog()
{
delete ui;
}
void joystickDialog::joystickOpen(int stick) {
if (stick == -1) {
this->close();
void joystickDialog::loadJoysticks(int stick)
{
QStringList joystickNames;
bool found = false;
ui->joystickCB->clear();
joystickNames << tr("No joysticks found");
joystick = new Joystick(0, false, 0, 0);
if ( joystick ) {
if ( joystick->joystickNames.count() > 0 ) {
joystickNames = joystick->joystickNames;
found = true;
}
joystick->close();
}
ui->joystickCB->insertItems(0, joystickNames);
if (found && stick < joystickNames.size())
ui->joystickCB->setCurrentIndex(stick);
else if (!found)
ui->joystickCB->setCurrentIndex(0);
joystickSetEnabled(found && ui->joystickChkB->isChecked());
}
void joystickDialog::joystickOpen(int stick)
{
if (stick == -1 || !ui->joystickChkB->isChecked() || !ui->joystickCB->isEnabled()) {
ui->calibrationWidget->setDisabled(true);
return;
}
ui->calibrationWidget->setEnabled(true);
ui->nextButton->setEnabled(true);
joystick = new Joystick(this, 1, false, 0);
int numAxes=std::min(joystick->numAxes,8);
if (joystick) {
joystick->open(stick);
if (joystick && joystick->open(stick)) {
numAxes = std::min(joystick->numAxes, MAX_JOYSTICKS);
for (int j=0; j<numAxes; j++) {
joystick->sensitivities[j] = 0;
joystick->deadzones[j]=20;
joystick->sensitivities[j] = 0;
joystick->deadzones[j]=20;
}
}
else {
QMessageBox::critical(this, tr("Error"), tr("Cannot open joystick."));
this->close();
}
}
void joystickDialog::onjoystickAxisValueChanged(int axis, int value) {
if (value > jscal[axis][2]) {
jscal[axis][2] = value;
}
if (value < jscal[axis][0]) {
jscal[axis][0] = value;
}
QSlider * sl[]= {ui->Ch_1, ui->Ch_2, ui->Ch_3, ui->Ch_4, ui->Ch_5, ui->Ch_6, ui->Ch_7, ui->Ch_8};
QCheckBox * ib[]={ui->ChInv_1, ui->ChInv_2, ui->ChInv_3, ui->ChInv_4, ui->ChInv_5, ui->ChInv_6, ui->ChInv_7, ui->ChInv_8};
if (ib[axis]->isChecked()) {
sl[axis]->setInvertedAppearance(true);
} else {
sl[axis]->setInvertedAppearance(false);
}
sl[axis]->setMinimum(jscal[axis][0]);
sl[axis]->setMaximum(jscal[axis][2]);
sl[axis]->setValue(value);
void joystickDialog::joystickSetEnabled(bool enable)
{
ui->joystickCB->setEnabled(enable);
ui->calibrationWidget->setEnabled(enable);
ui->nextButton->setEnabled(enable && step < 4);
ui->backButton->setEnabled(enable && step);
if (enable)
joystickOpen(ui->joystickCB->currentIndex());
}
void joystickDialog::on_nextButton_clicked() {
int numAxes=std::min(joystick->numAxes,8);
void joystickDialog::onjoystickAxisValueChanged(int axis, int value)
{
if (axis >= MAX_JOYSTICKS)
return;
if (started) {
if (value > jscal[axis][2])
jscal[axis][2] = value;
else if (value < jscal[axis][0])
jscal[axis][0] = value;
sliders[axis]->setMinimum(jscal[axis][0]);
sliders[axis]->setMaximum(jscal[axis][2]);
}
sliders[axis]->setInvertedAppearance(invert[axis]->isChecked());
sliders[axis]->setValue(value);
}
void joystickDialog::loadStep()
{
switch (step) {
case 0:
ui->howtoLabel->setText(tr("Place sticks and pots in middle position.\nPress next when done"));
step++;
ui->howtoLabel->setText(tr("Press the Start button to start the stick range calibration procedure.\n" \
"You may change the channel assignments or inversion at any time."));
ui->backButton->setDisabled(true);
break;
case 1:
for (int i=0; i<numAxes; i++) {
jscal[i][1]=0;
for (int j=0; j<100;j++) {
jscal[i][1]+=joystick->getAxisValue(i);
}
jscal[i][1]/=100;
}
ui->howtoLabel->setText(tr("Map joystick channels to controls using comboboxes.\nPress next when done"));
step++;
foreach(QComboBox *cb, findChildren<QComboBox *>(QRegExp("jsmapCB_[0-9]+"))) {
cb->setEnabled(true);
}
started = true;
ui->howtoLabel->setText(tr("Move sticks and pots in every direction making full movement\nPress Next when finished"));
ui->nextButton->setText(tr("Next"));
ui->backButton->setDisabled(true);
break;
case 2:
ui->howtoLabel->setText(tr("Check inversion checkbox to get maximum at top-right position.\nPress next when done"));
step++;
foreach(QCheckBox *ib, findChildren<QCheckBox *>(QRegExp("ChInv_[0-9]+"))) {
ib->setEnabled(true);
}
ui->howtoLabel->setText(tr("Place sticks and pots in middle position.\nPress Next when done"));
ui->backButton->setEnabled(true);
ui->nextButton->setEnabled(true);
break;
case 3:
ui->howtoLabel->setText(tr("Press ok to save configuration\nPress cancel to abort joystick calibration"));
ui->okButton->setEnabled(true);
for (int i=0; i < numAxes; i++) {
jscal[i][1]=0;
for (int j=0; j < 100; j++) {
jscal[i][1] += joystick->getAxisValue(i);
}
jscal[i][1] /= 100;
}
ui->howtoLabel->setText(tr("Map joystick channels to controls using comboboxes.\nPress Next when done."));
ui->nextButton->setEnabled(true);
break;
case 4:
ui->howtoLabel->setText(tr("Check inversion checkbox to if necessary to reverse direction.\nPress Next when done."));
ui->nextButton->setEnabled(true);
break;
case 5:
ui->howtoLabel->setText(tr("Press OK to save configuration\nPress Cancel to abort joystick calibration without saving."));
ui->nextButton->setDisabled(true);
}
}
void joystickDialog::on_cancelButton_clicked() {
void joystickDialog::on_backButton_clicked()
{
if (step) {
--step;
loadStep();
}
}
void joystickDialog::on_nextButton_clicked()
{
if (step < 5){
++step;
loadStep();
}
}
void joystickDialog::on_cancelButton_clicked()
{
joystick->close();
this->close();
this->reject();
}
void joystickDialog::on_okButton_clicked()
{
int stickAssignments[MAX_JOYSTICKS] = {0};
foreach(QComboBox *cb, findChildren<QComboBox *>(QRegExp("jsmapCB_[0-9]+"))) {
int mappedValue = cb->currentIndex() - 1;
// qDebug() << "cb->currentIndex()" << mappedValue;
if (mappedValue >= 0 && mappedValue < MAX_JOYSTICKS) {
stickAssignments[mappedValue]++;
}
int stick;
g.jsSupport(ui->joystickChkB->isChecked());
g.jsCtrl(ui->joystickCB->currentIndex());
if (!g.jsSupport() || g.jsCtrl() < 0) {
this->accept();
return;
}
for (int i=0; i<MAX_JOYSTICKS; i++) {
if (stickAssignments[i] > 1) {
QMessageBox::critical(this, tr("Error"), tr("Duplicated stick assignment"));
if (started && step < 4) {
int resp = QMessageBox::warning(this, tr("Warning"), tr("Calibration not complete, save anyway?"),
QDialogButtonBox::Ok | QMessageBox::Default, QDialogButtonBox::Cancel | QMessageBox::Escape, QMessageBox::NoButton);
if (resp == QDialogButtonBox::Cancel)
return;
}
int stickAssignments[MAX_JOYSTICKS] = {0};
for (int i = 0; i < MAX_JOYSTICKS; ++i) {
stick = sticks[i]->currentIndex() - 1;
if (stick >= 0 && stick < MAX_JOYSTICKS && ++stickAssignments[stick] > 1) {
QMessageBox::critical(this, tr("Error"), tr("Duplicated stick assignment!"));
return;
}
}
joystick->close();
for (int i=0; i<MAX_JOYSTICKS; i++) {
for (int i = 0; i < MAX_JOYSTICKS; ++i)
g.joystick[i].remove();
}
QCheckBox * ib[]={ui->ChInv_1, ui->ChInv_2, ui->ChInv_3, ui->ChInv_4, ui->ChInv_5, ui->ChInv_6, ui->ChInv_7, ui->ChInv_8};
foreach(QComboBox *cb, findChildren<QComboBox *>(QRegExp("jsmapCB_[0-9]+"))) {
int axe=cb->objectName().mid(cb->objectName().lastIndexOf("_")+1).toInt()-1;
int stick=cb->currentIndex() - 1;
qDebug() << "joystick mapping " << cb->objectName() <<"axe:" << axe << "stick:" << stick;
for (int i = 0; i < MAX_JOYSTICKS; ++i) {
stick = sticks[i]->currentIndex() - 1;
qDebug() << "joystick mapping " << sticks[i]->objectName() << "axe:" << i << "stick:" << stick;
if (stick >= 0 && stick < MAX_JOYSTICKS) {
g.joystick[stick].stick_axe( axe );
g.joystick[stick].stick_max( jscal[axe][2] );
g.joystick[stick].stick_med( jscal[axe][1] );
g.joystick[stick].stick_min( jscal[axe][0] );
g.joystick[stick].stick_inv( ib[axe]->isChecked() ? 1 : 0 );
g.joystick[stick].stick_axe( i );
g.joystick[stick].stick_max( jscal[i][2] );
g.joystick[stick].stick_med( jscal[i][1] );
g.joystick[stick].stick_min( jscal[i][0] );
g.joystick[stick].stick_inv( invert[i]->isChecked() );
}
}
this->close();
this->accept();
}
+22 -10
View File
@@ -22,8 +22,13 @@
#define _JOYSTICKDIALOG_H_
#include <QtWidgets>
#include "appdata.h"
#include "joystick.h"
class QCheckBox;
class QComboBox;
class QSlider;
namespace Ui {
class joystickDialog;
}
@@ -32,25 +37,32 @@ class joystickDialog : public QDialog
{
Q_OBJECT
public:
public:
explicit joystickDialog(QWidget *parent = 0, int stick=-1);
~joystickDialog();
Joystick *joystick;
public slots:
void onjoystickAxisValueChanged(int axis, int value);
private:
private:
Ui::joystickDialog *ui;
void joystickOpen(int stick);
int jscal[8][3];
int jscal[MAX_JOYSTICKS][3];
QCheckBox * invert[MAX_JOYSTICKS];
QComboBox * sticks[MAX_JOYSTICKS];
QSlider * sliders[MAX_JOYSTICKS];
int step;
private slots:
int numAxes;
bool started;
private slots:
void loadJoysticks(int stick = -1);
void joystickOpen(int stick);
void joystickSetEnabled(bool enable);
void onjoystickAxisValueChanged(int axis, int value);
void loadStep();
void on_backButton_clicked();
void on_nextButton_clicked();
void on_cancelButton_clicked();
void on_okButton_clicked();
};
#endif // _JOYSTICKDIALOG_H_
File diff suppressed because it is too large Load Diff
@@ -31,6 +31,8 @@ class SimulatorInterface;
class LcdWidget;
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.
* It is responsible for hanlding all interactions with this part of the simulation (vs. common radio widgets like sticks/switches/knobs).
+89
View File
@@ -0,0 +1,89 @@
/*
* 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 SIMULATOR_H
#define SIMULATOR_H
#include <QColor>
#include <QDataStream>
#include <QDebug>
#include <QPair>
#include <QString>
#define SIMULATOR_OPTIONS_VERSION 1
namespace Simulator {
typedef QPair<QString, QString> keymapHelp_t;
}
struct SimulatorOptions
{
enum StartupDataTypes {
START_WITH_FILE = 0,
START_WITH_FOLDER,
START_WITH_SDPATH,
START_WITH_RAWDATA
};
private:
quint16 _version = SIMULATOR_OPTIONS_VERSION; // structure definition version
public:
// v1 fields
quint8 startupDataType = START_WITH_FILE;
QString firmwareId;
QString dataFile;
QString dataFolder;
QString sdPath;
QByteArray windowGeometry;
QList<QByteArray> controlsState; // saved switch/pot/stick settings
QColor lcdColor;
friend QDataStream & operator << (QDataStream &out, const SimulatorOptions & o)
{
out << o._version << o.startupDataType << o.firmwareId << o.dataFile << o.dataFolder
<< o.sdPath << o.windowGeometry << o.controlsState << o.lcdColor;
return out;
}
friend QDataStream & operator >> (QDataStream &in, SimulatorOptions & o)
{
if (o._version <= SIMULATOR_OPTIONS_VERSION) {
in >> o._version >> o.startupDataType >> o.firmwareId >> o.dataFile >> o.dataFolder
>> o.sdPath >> o.windowGeometry >> o.controlsState >> o.lcdColor;
}
return in;
}
friend QDebug operator << (QDebug d, const SimulatorOptions & o)
{
QDebugStateSaver saver(d);
d.nospace() << "SimulatorOptions: firmwareId=" << o.firmwareId << "; dataFile=" << o.dataFile << "; dataFolder=" << o.dataFolder
<< "; sdPath=" << o.sdPath << "; startupDataType=" << o.startupDataType;
return d;
}
};
Q_DECLARE_METATYPE(SimulatorOptions)
#endif // SIMULATOR_H
+317 -96
View File
@@ -30,12 +30,13 @@
#include "sdcard.h"
#include "simulateduiwidget.h"
#include "simulatorinterface.h"
//#include "storage.h"
#include "storage.h"
#include "telemetrysimu.h"
#include "trainersimu.h"
#include "virtualjoystickwidget.h"
#ifdef JOYSTICKS
#include "joystick.h"
#include "joystickdialog.h"
#endif
#include <QDebug>
@@ -71,7 +72,9 @@ SimulatorDialog::SimulatorDialog(QWidget * parent, SimulatorInterface *simulator
lastPhase(-1),
buttonPressed(0),
trimPressed(TRIM_NONE),
eepromDataFromFile(false),
startupFromFile(false),
deleteTempRadioData(false),
saveTempRadioData(false),
middleButtonPressed(false)
{
setWindowFlags(Qt::Window);
@@ -80,6 +83,10 @@ SimulatorDialog::SimulatorDialog(QWidget * parent, SimulatorInterface *simulator
traceCallbackInstance = this;
simulator->installTraceHook(traceCb);
// defaults
setRadioProfileId(radioProfileId);
setSdPath(g.profile[radioProfileId].sdPath());
setupUi();
}
@@ -98,8 +105,13 @@ SimulatorDialog::~SimulatorDialog()
void SimulatorDialog::setRadioProfileId(int value)
{
radioProfileId = value;
simulator->setVolumeGain(g.profile[radioProfileId].volumeGain());
setPaths(g.profile[radioProfileId].sdPath(), radioDataPath);
if (simulator)
simulator->setVolumeGain(g.profile[radioProfileId].volumeGain());
}
void SimulatorDialog::setSdPath(const QString & sdPath)
{
setPaths(sdPath, radioDataPath);
}
void SimulatorDialog::setDataPath(const QString & dataPath)
@@ -111,7 +123,8 @@ void SimulatorDialog::setPaths(const QString & sdPath, const QString & dataPath)
{
sdCardPath = sdPath;
radioDataPath = dataPath;
simulator->setSdPath(sdPath, dataPath);
if (simulator)
simulator->setSdPath(sdPath, dataPath);
}
void SimulatorDialog::setRadioSettings(const GeneralSettings settings)
@@ -119,35 +132,66 @@ void SimulatorDialog::setRadioSettings(const GeneralSettings settings)
radioSettings = settings;
}
void SimulatorDialog::setEepromData(const QByteArray & eeprom, bool fromFile)
/*
* This function can accept no parameters, a file name (QString is a QBA), or a data array. It will attempt to load radio settings data from one of
* several sources into a RadioData object, parse the data, and then pass it on as appropriate to the SimulatorInterface in start().
* If given no/blank <eeprom>, and setDataPath() was already called, then it will check that directory for "Horus-style" data files.
* If given a file name, set the <fromFile> parameter to 'true'. This will attempt to load radio settings from said file
* and later start the simulator interface in start() using the same data.
* If <eeprom> is a byte array of data, attempts to load radio settings from there and will also start the simulator interface
* with the same data when start() is called.
* If you already have a valid RadioData structure, call setRadioData() instead.
*/
void SimulatorDialog::setStartupData(const QByteArray & dataSource, bool fromFile)
{
RadioData simuData;
quint16 ret = 1;
QString error;
if (eeprom.isEmpty() && !radioDataPath.isEmpty()) {
// FIXME : need Storage classes to return error code, not just a message.
// If <eeprom> is blank but we have a data path, use that for individual radio/model files.
if (dataSource.isEmpty() && !radioDataPath.isEmpty()) {
// If directory structure already exists, try to load data from there.
// FIXME : need Storage class to return formal error code, not just a boolean, because it would be better
// to avoid hard-coding paths like "RADIO" here. E.g. did it fail due to no data at all, or corrupt data, or...?
if (QDir(QString(radioDataPath).append("/RADIO")).exists()) {
SdcardFormat sdcard(radioDataPath);
ret = sdcard.load(simuData);
if (!ret)
if (!(ret = sdcard.load(simuData)))
error = sdcard.error();
}
}
// Supposedly we're being given a file name to use, try that out.
else if (fromFile) {
// Storage store = Storage(QString(eeprom));
// if (!(ret = store.load(simuData))) {
// error = store.error();
// }
QFile file;
QByteArray ba;
file.setFileName(QString(eeprom));
if (file.exists() && file.open(QIODevice::ReadOnly) && !(ba = file.readAll()).isEmpty()) {
ret = firmware->getEEpromInterface()->load(simuData, (uint8_t *)ba.constData(), getEEpromSize(m_board));
Storage store = Storage(QString(dataSource));
if ((ret = store.load(simuData))) {
if (IS_HORUS(m_board)) {
// save the data to a temp folder
if (!(ret = useTempDataPath(true, true))) // save data back to file on simulator close
error = tr("Error: Could not save data to temporary directory in '%1'").arg(QDir::tempPath());
else
ret = saveRadioData(&simuData, radioDataPath, &error);
}
else if (QString(dataSource).endsWith(".otx", Qt::CaseInsensitive)) {
// FIXME : Right now there's no way to read data back into the .otx file after simulation finishes.
setRadioData(&simuData);
return;
}
else {
startupData = dataSource; // save the file name for start()
}
}
else {
error = store.error();
}
}
else if (!eeprom.isEmpty()) {
ret = firmware->getEEpromInterface()->load(simuData, (uint8_t *)eeprom.constData(), getEEpromSize(m_board));
// Assume a byte array of radio data was passed, load it.
else if (!dataSource.isEmpty()) {
ret = firmware->getEEpromInterface()->load(simuData, (uint8_t *)dataSource.constData(), getEEpromSize(m_board));
startupData = dataSource; // save the data for start()
}
// we're :-(
else {
ret = 0;
error = tr("Could not determine startup data source.");
}
if (!ret) {
@@ -158,8 +202,7 @@ void SimulatorDialog::setEepromData(const QByteArray & eeprom, bool fromFile)
}
radioSettings = simuData.generalSettings;
eepromData = eeprom;
eepromDataFromFile = fromFile;
startupFromFile = fromFile;
}
void SimulatorDialog::setRadioData(RadioData * radioData)
@@ -167,15 +210,108 @@ void SimulatorDialog::setRadioData(RadioData * radioData)
if (radioDataPath.isEmpty()) {
QByteArray eeprom(getEEpromSize(m_board), 0);
if (firmware->getEEpromInterface()->save((uint8_t *)eeprom.data(), *radioData, 0, firmware->getCapability(SimulatorVariant)) > 0)
setEepromData(eeprom, false);
setStartupData(eeprom, false);
}
else {
SdcardFormat sdcard(radioDataPath);
sdcard.write(*radioData);
saveRadioData(radioData, radioDataPath);
radioSettings = radioData->generalSettings;
}
}
void SimulatorDialog::setOptions(SimulatorOptions & options, bool withSave)
{
setSdPath(options.sdPath);
if (options.startupDataType == SimulatorOptions::START_WITH_FOLDER && !options.dataFolder.isEmpty()) {
setDataPath(options.dataFolder);
setStartupData();
}
else if (options.startupDataType == SimulatorOptions::START_WITH_SDPATH && !options.sdPath.isEmpty()) {
setDataPath(options.sdPath);
setStartupData();
}
else if (options.startupDataType == SimulatorOptions::START_WITH_FILE && !options.dataFile.isEmpty()) {
setStartupData(options.dataFile.toLocal8Bit(), true);
}
if (withSave)
g.profile[radioProfileId].simulatorOptions(options);
}
bool SimulatorDialog::saveRadioData(RadioData * radioData, const QString & path, QString * error)
{
QString dir = path;
if (dir.isEmpty())
dir = radioDataPath;
if (radioData && !dir.isEmpty()) {
SdcardFormat sdcard(dir);
bool ret = sdcard.write(*radioData);
if (!ret && error)
*error = sdcard.error();
return ret;
}
return false;
}
bool SimulatorDialog::useTempDataPath(bool deleteOnClose, bool saveOnClose)
{
if (deleteTempRadioData)
deleteTempData();
QTemporaryDir tmpDir(QDir::tempPath() + "/otx-XXXXXX");
if (tmpDir.isValid()) {
setDataPath(tmpDir.path());
tmpDir.setAutoRemove(false);
deleteTempRadioData = deleteOnClose;
saveTempRadioData = saveOnClose;
qDebug() << __FILE__ << __LINE__ << "Created temporary settings directory" << radioDataPath
<< "with delteOnClose:" << deleteOnClose << "with saveOnClose:" << saveOnClose;
return true;
}
else {
qDebug() << __FILE__ << __LINE__ << "ERROR : Failed to create temporary settings directory" << radioDataPath;
return false;
}
}
// This will save radio data from temporary folder structure back into an .otx file, eg. for Horus.
bool SimulatorDialog::saveTempData()
{
bool ret = false;
QString error;
QString file = g.profile[radioProfileId].simulatorOptions().dataFile;
if (!saveTempRadioData || radioDataPath.isEmpty() || file.isEmpty())
return ret;
RadioData radioData;
SdcardFormat sdcard(radioDataPath);
if (!(ret = sdcard.load(radioData))) {
error = sdcard.error();
}
else {
Storage store(file);
if (!(ret = store.write(radioData)))
error = store.error();
else
qDebug() << __FILE__ << __LINE__ << "Saved radio data to file" << file;
}
if (!ret)
QMessageBox::critical(this, tr("Data Save Error"), error);
return ret;
}
void SimulatorDialog::deleteTempData()
{
if (!radioDataPath.isEmpty()) {
QDir tpath(radioDataPath);
qDebug() << __FILE__ << __LINE__ << "Deleting temporary settings directory" << tpath.absolutePath();
tpath.removeRecursively();
tpath.rmdir(radioDataPath); // for some reason this is necessary to remove the base folder
}
}
void SimulatorDialog::setUiAreaStyle(const QString & style)
{
ui->radioUiTab->setStyleSheet(style);
@@ -192,6 +328,31 @@ void SimulatorDialog::traceCallback(const char * text)
traceMutex.unlock();
}
/*
* Startup
*/
void SimulatorDialog::start()
{
setupRadioWidgets();
setupJoysticks();
setupOutputsDisplay();
setupGVarsDisplay();
restoreRadioWidgetsState();
setTrims();
if (startupData.isEmpty())
simulator->start((const char *)0);
else if (startupFromFile)
simulator->start(startupData.constData());
else
simulator->start(startupData, (flags & SIMULATOR_FLAGS_NOTX) ? false : true);
getValues();
setupTimer();
startupData.clear(); // this is safe because simulator->start() makes copy of data/discards the file name
}
/*
* Setup
*/
@@ -220,7 +381,6 @@ void SimulatorDialog::setupUi()
break;
}
// support for <QT5.5
foreach (keymapHelp_t item, *radioUiWidget->getKeymapHelp()) {
keymapHelp.append(item);
}
@@ -235,47 +395,6 @@ void SimulatorDialog::setupUi()
vJoyRight = new VirtualJoystickWidget(this, 'R');
ui->rightStickLayout->addWidget(vJoyRight);
#ifdef JOYSTICKS
if (g.jsSupport()) {
int count=0;
for (int j=0; j<8; j++){
int axe = g.joystick[j].stick_axe();
if (axe>=0 && axe<8) {
jsmap[axe]=j+1;
jscal[axe][0] = g.joystick[j].stick_min();
jscal[axe][1] = g.joystick[j].stick_med();
jscal[axe][2] = g.joystick[j].stick_max();
jscal[axe][3] = g.joystick[j].stick_inv();
count++;
}
}
if (count<3) {
QMessageBox::critical(this, tr("Warning"), tr("Joystick enabled but not configured correctly"));
}
if (g.jsCtrl()!=-1) {
joystick = new Joystick(this);
if (joystick) {
if (joystick->open(g.jsCtrl())) {
int numAxes=std::min(joystick->numAxes,8);
for (int j=0; j<numAxes; j++) {
joystick->sensitivities[j] = 0;
joystick->deadzones[j]=0;
}
//mode 1,3 -> THR on right
vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_Y, true);
vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_X, true);
vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_Y, true);
vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_X, true);
connect(joystick, SIGNAL(axisValueChanged(int, int)), this, SLOT(onjoystickAxisValueChanged(int, int)));
}
else {
QMessageBox::critical(this, tr("Warning"), tr("Cannot open joystick, joystick disabled"));
}
}
}
}
#endif
ui->tabWidget->setCurrentIndex(flags & SIMULATOR_FLAGS_NOTX);
connect(vJoyLeft, SIGNAL(trimButtonPressed(int)), this, SLOT(onTrimPressed(int)));
@@ -287,30 +406,43 @@ void SimulatorDialog::setupUi()
connect(vJoyRight, SIGNAL(trimSliderMoved(int,int)), this, SLOT(onTrimSliderMoved(int,int)));
connect(ui->btn_help, SIGNAL(released()), this, SLOT(showHelp()));
connect(ui->btn_joystickDialog, SIGNAL(released()), this, SLOT(openJoystickDialog()));
connect(ui->btn_telemSim, SIGNAL(released()), this, SLOT(openTelemetrySimulator()));
connect(ui->btn_trainerSim, SIGNAL(released()), this, SLOT(openTrainerSimulator()));
connect(ui->btn_debugConsole, SIGNAL(released()), this, SLOT(openDebugOutput()));
connect(ui->btn_luaReload, SIGNAL(released()), this, SLOT(luaReload()));
connect(ui->btn_screenshot, SIGNAL(released()), radioUiWidget, SLOT(captureScreenshot()));
// Hide some main UI buttons based on board capabilities, and add keymap help texts.
keymapHelp.append(keymapHelp_t(ui->btn_help->shortcut().toString(QKeySequence::NativeText), ui->btn_help->statusTip()));
if (!firmware->getCapability(Capability(LuaInputsPerScript))) // hackish! but using "LuaScripts" checks for id "lua" in fw.
keymapHelp.append(keymapHelp_t(ui->btn_telemSim->shortcut().toString(QKeySequence::NativeText), ui->btn_telemSim->statusTip()));
else
ui->btn_luaReload->hide();
keymapHelp.append(keymapHelp_t(ui->btn_trainerSim->shortcut().toString(QKeySequence::NativeText), ui->btn_trainerSim->statusTip()));
keymapHelp.append(keymapHelp_t(ui->btn_debugConsole->shortcut().toString(QKeySequence::NativeText), ui->btn_debugConsole->statusTip()));
#ifdef JOYSTICKS
keymapHelp.append(keymapHelp_t(ui->btn_joystickDialog->shortcut().toString(QKeySequence::NativeText), ui->btn_joystickDialog->statusTip()));
#else
ui->btn_joystickDialog->hide();
#endif
if (firmware->getCapability(Capability(SportTelemetry)))
keymapHelp.append(keymapHelp_t(ui->btn_luaReload->shortcut().toString(QKeySequence::NativeText), ui->btn_luaReload->statusTip()));
keymapHelp.append(keymapHelp_t(ui->btn_telemSim->shortcut().toString(QKeySequence::NativeText), ui->btn_luaReload->statusTip()));
else
ui->btn_telemSim->hide();
keymapHelp.append(keymapHelp_t(ui->btn_trainerSim->shortcut().toString(QKeySequence::NativeText), ui->btn_trainerSim->statusTip()));
keymapHelp.append(keymapHelp_t(ui->btn_debugConsole->shortcut().toString(QKeySequence::NativeText), ui->btn_debugConsole->statusTip()));
if (!firmware->getCapability(Capability(LuaInputsPerScript))) // hackish! but using "LuaScripts" checks for id "lua" in fw.
keymapHelp.append(keymapHelp_t(ui->btn_luaReload->shortcut().toString(QKeySequence::NativeText), ui->btn_telemSim->statusTip()));
else
ui->btn_luaReload->hide();
keymapHelp.append(keymapHelp_t(ui->btn_screenshot->shortcut().toString(QKeySequence::NativeText), ui->btn_screenshot->statusTip()));
}
void SimulatorDialog::setupRadioWidgets()
{
int i, midpos, aIdx;
int i, midpos, aIdx, wval;
QString wname;
Board::Type board = firmware->getBoard();
@@ -335,16 +467,20 @@ void SimulatorDialog::setupRadioWidgets()
// switches
Board::SwitchInfo switchInfo;
Board::SwitchType swcfg;
// FIXME : CPN_MAX_SWITCHES == 32 but GeneralSettings::switchConfig[18] !!
for (i = 0; i < firmware->getCapability(Capability(Switches)) && i < 18 /*CPN_MAX_SWITCHES*/; ++i) {
if (radioSettings.switchConfig[i] == Board::SWITCH_NOT_AVAILABLE)
continue;
swcfg = Board::SwitchType(radioSettings.switchConfig[i]);
wval = (swcfg == Board::SWITCH_3POS ? -1 : 0);
if ((wname = QString(radioSettings.switchName[i])).isEmpty()) {
switchInfo = getSwitchInfo(board, i);
wname = QString(switchInfo.name);
}
RadioSwitchWidget * sw = new RadioSwitchWidget(Board::SwitchType(radioSettings.switchConfig[i]), wname, 0, ui->radioWidgetsHT);
RadioSwitchWidget * sw = new RadioSwitchWidget(swcfg, wname, wval, ui->radioWidgetsHT);
sw->setIndex(i);
ui->radioWidgetsHTLayout->addWidget(sw);
switches.append(sw);
@@ -532,6 +668,56 @@ QFrame * SimulatorDialog::createLogicalSwitch(QWidget * parent, int switchNo, QV
return swtch;
}
void SimulatorDialog::setupJoysticks()
{
#ifdef JOYSTICKS
static bool joysticksEnabled = false;
if (g.jsSupport() && g.jsCtrl() > -1) {
int count=0, axe;
for (int j=0; j < MAX_JOYSTICKS; j++){
axe = g.joystick[j].stick_axe();
if (axe >= 0 && axe < MAX_JOYSTICKS) {
jsmap[axe] = j + 1;
jscal[axe][0] = g.joystick[j].stick_min();
jscal[axe][1] = g.joystick[j].stick_med();
jscal[axe][2] = g.joystick[j].stick_max();
jscal[axe][3] = g.joystick[j].stick_inv();
count++;
}
}
if (count<3) {
QMessageBox::critical(this, tr("Warning"), tr("Joystick enabled but not configured correctly"));
return;
}
joystick = new Joystick(this);
if (joystick && joystick->open(g.jsCtrl())) {
int numAxes = std::min(joystick->numAxes, MAX_JOYSTICKS);
for (int j=0; j<numAxes; j++) {
joystick->sensitivities[j] = 0;
joystick->deadzones[j] = 0;
}
//mode 1,3 -> THR on right
vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_Y, true);
vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_X, true);
vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_Y, true);
vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_X, true);
connect(joystick, SIGNAL(axisValueChanged(int, int)), this, SLOT(onjoystickAxisValueChanged(int, int)));
joysticksEnabled = true;
}
else {
QMessageBox::critical(this, tr("Warning"), tr("Cannot open joystick, joystick disabled"));
}
}
else if (joysticksEnabled && joystick) {
disconnect(joystick, 0, this, 0);
vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_Y, false);
vJoyRight->setStickConstraint(VirtualJoystickWidget::HOLD_X, false);
vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_Y, false);
vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_X, false);
}
#endif
}
void SimulatorDialog::setupTimer()
{
timer = new QTimer(this);
@@ -539,26 +725,44 @@ void SimulatorDialog::setupTimer()
timer->start(10);
}
/*
* Startup
*/
void SimulatorDialog::start()
void SimulatorDialog::restoreRadioWidgetsState()
{
setupRadioWidgets();
setupOutputsDisplay();
setupGVarsDisplay();
setTrims();
RadioWidget::RadioWidgetState state;
QMap<int, QByteArray> switchesMap;
QMap<int, QByteArray> analogsMap;
QList<QByteArray> states = g.profile[radioProfileId].simulatorOptions().controlsState;
if (eepromData.isEmpty())
simulator->start((const char *)0);
else if (eepromDataFromFile)
simulator->start(eepromData.constData());
else
simulator->start(eepromData, (flags & SIMULATOR_FLAGS_NOTX) ? false : true);
foreach (QByteArray ba, states) {
QDataStream stream(ba);
stream >> state;
if (state.type == RadioWidget::RADIO_WIDGET_SWITCH)
switchesMap.insert(state.index, ba);
else
analogsMap.insert(state.index, ba);
}
setupTimer();
eepromData.clear(); // this is safe because simulator->start() makes copy of data/discards the file name
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()));
}
}
QList<QByteArray> SimulatorDialog::saveRadioWidgetsState()
{
QList<QByteArray> states;
for (int i = 0; i < analogs.size(); ++i)
states.append(analogs[i]->getStateData());
for (int i = 0; i < switches.size(); ++i)
states.append(switches[i]->getStateData());
return states;
}
/*
@@ -687,14 +891,21 @@ void SimulatorDialog::closeEvent(QCloseEvent *)
{
simulator->stop();
timer->stop();
g.profile[radioProfileId].simuWinGeo(saveGeometry());
SimulatorOptions opts = g.profile[radioProfileId].simulatorOptions();
opts.windowGeometry = saveGeometry();
opts.controlsState = saveRadioWidgetsState();
g.profile[radioProfileId].simulatorOptions(opts);
if (saveTempRadioData)
saveTempData();
if (deleteTempRadioData)
deleteTempData();
}
void SimulatorDialog::showEvent(QShowEvent *)
{
static bool firstShow = true;
if (firstShow) {
restoreGeometry(g.profile[radioProfileId].simuWinGeo());
restoreGeometry(g.profile[radioProfileId].simulatorOptions().windowGeometry);
// The stick position needs to be set after the final show event, otherwise resizes during dialog creation will screw it up.
if (radioSettings.stickMode & 1) {
@@ -803,6 +1014,16 @@ void SimulatorDialog::openTrainerSimulator()
}
}
void SimulatorDialog::openJoystickDialog()
{
#ifdef JOYSTICKS
joystickDialog * jd = new joystickDialog(this);
if (jd->exec() == QDialog::Accepted)
setupJoysticks();
jd->deleteLater();
#endif
}
void SimulatorDialog::openDebugOutput()
{
// allow only one instance, but install signal handler to catch dialog destruction just in case
+21 -5
View File
@@ -24,6 +24,7 @@
#include "constants.h"
#include "helpers.h"
#include "radiodata.h"
#include "simulator.h"
#include <QDialog>
#include <QVector>
@@ -39,8 +40,6 @@
#define CBEEP_ON "QLabel { background-color: #FF364E }"
#define CBEEP_OFF "QLabel { }"
typedef QPair<QString, QString> keymapHelp_t;
void traceCb(const char * text);
class Firmware;
@@ -66,6 +65,8 @@ namespace Ui {
class SimulatorDialog;
}
using namespace Simulator;
class SimulatorDialog : public QDialog
{
Q_OBJECT
@@ -75,22 +76,34 @@ class SimulatorDialog : public QDialog
virtual ~SimulatorDialog();
void setRadioProfileId(int value);
void setSdPath(const QString & sdPath);
void setDataPath(const QString & dataPath);
void setPaths(const QString & sdPath, const QString & dataPath);
void setRadioSettings(const GeneralSettings settings);
void setEepromData(const QByteArray & eeprom = NULL, bool fromFile = false);
void setStartupData(const QByteArray & dataSource = NULL, bool fromFile = false);
void setRadioData(RadioData * radioData);
void setOptions(SimulatorOptions & options, bool withSave = true);
bool saveRadioData(RadioData * radioData, const QString & path = "", QString * error = NULL);
bool useTempDataPath(bool deleteOnClose = true, bool saveOnClose = false);
bool saveTempData();
void deleteTempData();
void setUiAreaStyle(const QString & style);
void traceCallback(const char * text);
void start();
QString getSdPath() const { return sdCardPath; }
QString getDataPath() const { return radioDataPath; }
private:
void setupUi();
void setupRadioWidgets();
void setupOutputsDisplay();
void setupGVarsDisplay();
void setupJoysticks();
QFrame * createLogicalSwitch(QWidget * parent, int switchNo, QVector<QLabel *> & labels);
void setupTimer();
void restoreRadioWidgetsState();
QList<QByteArray> saveRadioWidgetsState();
void setValues();
void getValues();
@@ -125,14 +138,16 @@ class SimulatorDialog : public QDialog
QString sdCardPath;
QString radioDataPath;
QByteArray eepromData;
QByteArray startupData;
Board::Type m_board;
quint8 flags;
int radioProfileId;
int lastPhase;
int buttonPressed;
int trimPressed;
bool eepromDataFromFile;
bool startupFromFile;
bool deleteTempRadioData;
bool saveTempRadioData;
bool middleButtonPressed;
#ifdef JOYSTICKS
@@ -155,6 +170,7 @@ class SimulatorDialog : public QDialog
void centerSticks();
void openTelemetrySimulator();
void openTrainerSimulator();
void openJoystickDialog();
void openDebugOutput();
void updateDebugOutput();
void luaReload();
+20 -4
View File
@@ -246,13 +246,29 @@
<string>Show Help/Keymap</string>
</property>
<property name="text">
<string>Keymap</string>
<string>Keymap Help</string>
</property>
<property name="shortcut">
<string>F1</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_joystickDialog">
<property name="toolTip">
<string>Open the Joystick Settings window (F3)</string>
</property>
<property name="statusTip">
<string>Telemetry Simulator</string>
</property>
<property name="text">
<string>Joystick Config.</string>
</property>
<property name="shortcut">
<string>F3</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_telemSim">
<property name="toolTip">
@@ -262,7 +278,7 @@
<string>Telemetry Simulator</string>
</property>
<property name="text">
<string>Telemetry Simulator</string>
<string>Telemetry Sim.</string>
</property>
<property name="shortcut">
<string>F4</string>
@@ -278,7 +294,7 @@
<string>Trainer Simulator</string>
</property>
<property name="text">
<string>Trainer Simulator</string>
<string>Trainer Sim.</string>
</property>
<property name="shortcut">
<string>F5</string>
@@ -310,7 +326,7 @@
<string>Reload Lua Scripts</string>
</property>
<property name="text">
<string>Reload Lua Scripts</string>
<string>Lua Reload</string>
</property>
<property name="shortcut">
<string>F7</string>
@@ -0,0 +1,257 @@
/*
* 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 "simulatorstartupdialog.h"
#include "ui_simulatorstartupdialog.h"
#include "appdata.h"
#include "constants.h"
#include "eeprominterface.h"
#include "helpers.h"
#include <QFileDialog>
extern AppData g;
extern QMap<QString, SimulatorFactory *> registered_simulators;
SimulatorStartupDialog::SimulatorStartupDialog(SimulatorOptions * options, int * profId, QWidget *parent) :
QDialog(parent),
ui(new Ui::SimulatorStartupDialog),
m_options(options),
m_profileId(profId)
{
ui->setupUi(this);
QMapIterator<int, QString> pi(g.getActiveProfiles());
while (pi.hasNext()) {
pi.next();
ui->radioProfile->addItem(pi.value(), pi.key());
}
ui->radioType->addItems(registered_simulators.keys());
ui->optGrp_dataSource->setId(ui->optFile, SimulatorOptions::START_WITH_FILE);
ui->optGrp_dataSource->setId(ui->optFolder, SimulatorOptions::START_WITH_FOLDER);
ui->optGrp_dataSource->setId(ui->optSdPath, SimulatorOptions::START_WITH_SDPATH);
CompanionIcon icon("open.png");
ui->btnSelectDataFile->setIcon(icon);
ui->btnSelectDataFolder->setIcon(icon);
ui->btnSelectSdPath->setIcon(icon);
QObject::connect(ui->radioProfile, SIGNAL(currentIndexChanged(int)), this, SLOT(onRadioProfileChanged(int)));
QObject::connect(ui->radioType, SIGNAL(currentIndexChanged(int)), this, SLOT(onRadioTypeChanged(int)));
QObject::connect(ui->btnSelectDataFile, &QToolButton::clicked, this, &SimulatorStartupDialog::onDataFileSelect);
QObject::connect(ui->btnSelectDataFolder, &QToolButton::clicked, this, &SimulatorStartupDialog::onDataFolderSelect);
QObject::connect(ui->btnSelectSdPath, &QToolButton::clicked, this, &SimulatorStartupDialog::onSdPathSelect);
loadRadioProfile(*m_profileId);
}
SimulatorStartupDialog::~SimulatorStartupDialog()
{
delete ui;
}
void SimulatorStartupDialog::changeEvent(QEvent *e)
{
QDialog::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}
// FIXME : need a better way to check for this
bool SimulatorStartupDialog::usesCategorizedStorage(const QString & name)
{
return name.contains("horus", Qt::CaseInsensitive);
}
bool SimulatorStartupDialog::usesCategorizedStorage()
{
return usesCategorizedStorage(ui->radioType->currentText());
}
QString SimulatorStartupDialog::findRadioId(const QString & str)
{
QString radioId(str);
int pos = str.indexOf("-");
if (pos > 0) {
radioId = str.mid(pos+1);
pos = radioId.lastIndexOf("-");
if (pos > 0) {
radioId = radioId.mid(0, pos);
}
}
return radioId;
}
// TODO : this could be smarter and actually look for a matching file in the folder
QString SimulatorStartupDialog::radioEepromFileName(const QString & firmwareId, QString folder)
{
QString eepromFileName = "", ext = "bin";
if (folder.isEmpty())
folder = g.eepromDir();
QString radioId = findRadioId(firmwareId);
if (usesCategorizedStorage(radioId))
ext = "otx";
eepromFileName = QString("eeprom-%1.%2").arg(radioId, ext);
eepromFileName = QDir(folder).filePath(eepromFileName.toLatin1());
// qDebug() << "radioId" << radioId << "eepromFileName" << eepromFileName;
return eepromFileName;
}
void SimulatorStartupDialog::updateContainerTypes(void)
{
static int oldstate = -1;
int state = usesCategorizedStorage();
if (state == oldstate)
return;
oldstate = state;
ui->wdgt_dataSource->setVisible(state);
ui->layout_options->labelForField(ui->wdgt_dataSource)->setVisible(state);
ui->wdgt_dataFolder->setVisible(state);
ui->layout_options->labelForField(ui->wdgt_dataFolder)->setVisible(state);
if (!state || ui->optGrp_dataSource->checkedId() < 0)
ui->optFile->setChecked(true);
}
void SimulatorStartupDialog::loadRadioProfile(int id)
{
QString tmpstr;
int i;
if (id < 0 || !g.getActiveProfiles().contains(id))
return;
i = ui->radioProfile->findData(id);
if (i > -1 && ui->radioProfile->currentIndex() != i) {
ui->radioProfile->setCurrentIndex(i);
return;
}
*m_options = g.profile[id].simulatorOptions();
tmpstr = m_options->firmwareId;
if (tmpstr.isEmpty() && !g.profile[id].fwType().isEmpty())
tmpstr = g.profile[id].fwType();
else if (getSimulatorFactory(tmpstr))
tmpstr = getSimulatorFactory(tmpstr)->name();
i = ui->radioType->findText(findRadioId(tmpstr), Qt::MatchFixedString | Qt::MatchContains);
if (i > -1)
ui->radioType->setCurrentIndex(i);
tmpstr = m_options->dataFile;
if (!tmpstr.isEmpty())
ui->dataFile->setText(tmpstr);
tmpstr = m_options->dataFolder;
if (tmpstr.isEmpty())
tmpstr = g.eepromDir();
ui->dataFolder->setText(tmpstr);
tmpstr = m_options->sdPath;
if (tmpstr.isEmpty())
tmpstr = g.profile[id].sdPath();
ui->sdPath->setText(tmpstr);
foreach (QAbstractButton * btn, ui->optGrp_dataSource->buttons()) {
if (ui->optGrp_dataSource->id(btn) == m_options->startupDataType) {
btn->setChecked(true);
break;
}
};
updateContainerTypes();
}
void SimulatorStartupDialog::accept()
{
*m_profileId = ui->radioProfile->currentData().toInt();
m_options->firmwareId = ui->radioType->currentText();
m_options->dataFile = ui->dataFile->text();
m_options->dataFolder = ui->dataFolder->text();
m_options->sdPath = ui->sdPath->text();
m_options->startupDataType = ui->optGrp_dataSource->checkedId();
QDialog::accept();
}
void SimulatorStartupDialog::onRadioProfileChanged(int index)
{
if (index < 0)
return;
loadRadioProfile(ui->radioProfile->currentData().toInt());
}
void SimulatorStartupDialog::onRadioTypeChanged(int index)
{
if (index < 0)
return;
ui->dataFile->setText(radioEepromFileName(ui->radioType->currentText()));
updateContainerTypes();
}
void SimulatorStartupDialog::onDataFileSelect(bool)
{
QString filter = tr(EEPROM_FILES_FILTER) + tr("All files (*.*)");
QString file = QFileDialog::getSaveFileName(this, QObject::tr("Select a data file"), ui->dataFile->text(),
filter, NULL, QFileDialog::DontConfirmOverwrite);
if (!file.isEmpty()) {
ui->dataFile->setText(file);
ui->optFile->setChecked(true);
}
}
void SimulatorStartupDialog::onDataFolderSelect(bool)
{
QString folder = QFileDialog::getExistingDirectory(this, QObject::tr("Select Data Directory"),
ui->dataFolder->text(), QFileDialog::DontUseNativeDialog);
if (!folder.isEmpty()) {
ui->dataFolder->setText(folder);
if (usesCategorizedStorage())
ui->optFolder->setChecked(true);
}
}
void SimulatorStartupDialog::onSdPathSelect(bool)
{
QString folder = QFileDialog::getExistingDirectory(this, QObject::tr("Select SD Card Image Folder"),
ui->sdPath->text(), QFileDialog::DontUseNativeDialog);
if (!folder.isEmpty()) {
ui->sdPath->setText(folder);
if (usesCategorizedStorage())
ui->optSdPath->setChecked(true);
}
}
@@ -0,0 +1,69 @@
/*
* 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 SIMULATORSTARTUPDIALOG_H
#define SIMULATORSTARTUPDIALOG_H
#include "simulator.h"
#include <QDialog>
namespace Ui {
class SimulatorStartupDialog;
}
using namespace Simulator;
class SimulatorStartupDialog : public QDialog
{
Q_OBJECT
public:
explicit SimulatorStartupDialog(SimulatorOptions * options, int * profId, QWidget *parent = 0);
~SimulatorStartupDialog();
static bool usesCategorizedStorage(const QString & name);
static QString findRadioId(const QString & str);
static QString radioEepromFileName(const QString & firmwareId, QString folder = "");
void updateContainerTypes();
public slots:
virtual void accept();
void loadRadioProfile(int id);
void onRadioProfileChanged(int index);
void onRadioTypeChanged(int index);
void onDataFileSelect(bool);
void onDataFolderSelect(bool);
void onSdPathSelect(bool);
protected:
bool usesCategorizedStorage();
void changeEvent(QEvent *e);
private:
Ui::SimulatorStartupDialog *ui;
SimulatorOptions * m_options;
int * m_profileId;
};
#endif // SIMULATORSTARTUPDIALOG_H
@@ -0,0 +1,357 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SimulatorStartupDialog</class>
<widget class="QDialog" name="SimulatorStartupDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>488</width>
<height>219</height>
</rect>
</property>
<property name="windowTitle">
<string>OpenTX Simulator - Startup Options</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>6</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Simulator Startup Options:</string>
</property>
<layout class="QFormLayout" name="layout_options">
<property name="labelAlignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="horizontalSpacing">
<number>5</number>
</property>
<property name="verticalSpacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>7</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Radio Profile:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="radioProfile">
<property name="toolTip">
<string>Existing radio profiles are shown here.&lt;br /&gt;
Create or edit profiles using the Companion application.</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Radio Type:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="radioType">
<property name="toolTip">
<string>Existing radio simulators are shown here.&lt;br /&gt;
The radio type specified in the selected profile is used by default.</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Data Source:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Data File:</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Data Folder:</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>SD Image Path:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QWidget" name="wdgt_dataFile" native="true">
<layout class="QHBoxLayout" name="layout_dataFile" stretch="1,0">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="dataFile">
<property name="toolTip">
<string>Radio data (.bin/.eeprom/.otx) image file to use. A new file with a default image will be created if necessary.&lt;br /&gt;
&lt;b&gt;NOTE&lt;/b&gt;: any existing EEPROM data incompatible with the selected radio type may be overwritten!</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnSelectDataFile">
<property name="toolTip">
<string>Select data file...</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../companion.qrc">
<normaloff>:/themes/monoblue/16/open.png</normaloff>:/themes/monoblue/16/open.png</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonIconOnly</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="4" column="1">
<widget class="QWidget" name="wdgt_dataFolder" native="true">
<layout class="QHBoxLayout" name="layout_dataFolder" stretch="1,0">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="dataFolder">
<property name="toolTip">
<string>Directory containing RADIO and MODELS folders to use.&lt;br /&gt;
New folder(s) with default radio/model will be created here if necessary.</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnSelectDataFolder">
<property name="toolTip">
<string>Select data folder...</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../companion.qrc">
<normaloff>:/themes/monoblue/16/open.png</normaloff>:/themes/monoblue/16/open.png</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonIconOnly</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="5" column="1">
<widget class="QWidget" name="wdgt_sdPath" native="true">
<layout class="QHBoxLayout" name="layout_sdPath" stretch="1,0">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="sdPath">
<property name="toolTip">
<string>Directory containing the SD card image to use.&lt;br/&gt;
The default is configured in the chosen Radio Profile.</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="btnSelectSdPath">
<property name="toolTip">
<string>Select SD card image folder...</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../companion.qrc">
<normaloff>:/themes/monoblue/16/open.png</normaloff>:/themes/monoblue/16/open.png</iconset>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonIconOnly</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="1">
<widget class="QWidget" name="wdgt_dataSource" native="true">
<property name="toolTip">
<string>Select which of the data sources (File/Folder/SD Card) you would like to start the simulator with.</string>
</property>
<layout class="QHBoxLayout" name="layout_dataSource">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QRadioButton" name="optFile">
<property name="text">
<string>File</string>
</property>
<attribute name="buttonGroup">
<string notr="true">optGrp_dataSource</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="optFolder">
<property name="text">
<string>Folder</string>
</property>
<attribute name="buttonGroup">
<string notr="true">optGrp_dataSource</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="optSdPath">
<property name="text">
<string>SD Path</string>
</property>
<attribute name="buttonGroup">
<string notr="true">optGrp_dataSource</string>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
<zorder>groupBox</zorder>
<zorder>buttonBox</zorder>
</widget>
<resources>
<include location="../companion.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>SimulatorStartupDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>SimulatorStartupDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
<buttongroups>
<buttongroup name="optGrp_dataSource"/>
</buttongroups>
</ui>
@@ -30,12 +30,12 @@ class RadioFaderWidget : public RadioWidget
public:
explicit RadioFaderWidget(QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()) :
explicit RadioFaderWidget(QWidget * parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()) :
RadioWidget(parent, f)
{
init();
}
explicit RadioFaderWidget(const QString &labelText, int value = 0, QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()) :
explicit RadioFaderWidget(const QString & labelText, int value = 0, QWidget * parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()) :
RadioWidget(labelText, value, parent, f)
{
init();
@@ -22,7 +22,7 @@
#define _RADIOKNOBWIDGET_H_
#include "radiowidget.h"
#include "radiodata.h"
#include "boards.h"
#include <QDial>
#include <QMouseEvent>
@@ -68,7 +68,7 @@ class RadioKnobWidget : public RadioWidget
class KnobWidget : public QDial
{
friend class RadioKnobWidget;
friend class RadioKnobWidget;
public:
@@ -22,7 +22,7 @@
#define _RADIOSWITCHWIDGET_H_
#include "radiowidget.h"
#include "radiodata.h"
#include "boards.h"
#include <QSlider>
#include <QDebug>
@@ -33,15 +33,15 @@ class RadioSwitchWidget : public RadioWidget
public:
explicit RadioSwitchWidget(Board::SwitchType type = Board::SWITCH_3POS, QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()) :
explicit RadioSwitchWidget(Board::SwitchType type = Board::SWITCH_3POS, QWidget * parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()) :
RadioWidget(parent, f),
switchConfig(type)
swType(type)
{
init();
}
explicit RadioSwitchWidget(Board::SwitchType type, const QString &labelText, int value = 0, QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()) :
explicit RadioSwitchWidget(Board::SwitchType type, const QString & labelText, int value = 0, QWidget * parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()) :
RadioWidget(labelText, value, parent, f),
switchConfig(type)
swType(type)
{
init();
}
@@ -58,7 +58,7 @@ class RadioSwitchWidget : public RadioWidget
sl->setInvertedControls(true);
sl->setTickPosition(QSlider::TicksBothSides);
sl->setPageStep(1);
sl->setMinimum((switchConfig == Board::SWITCH_3POS ? -1 : 0));
sl->setMinimum((swType == Board::SWITCH_3POS ? -1 : 0));
sl->setMaximum(1);
sl->setTickInterval(1);
sl->setValue(m_value);
@@ -70,7 +70,7 @@ class RadioSwitchWidget : public RadioWidget
}
private:
Board::SwitchType switchConfig;
Board::SwitchType swType;
};
@@ -0,0 +1,201 @@
/*
* 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 "radiowidget.h"
RadioWidget::RadioWidget(QWidget * parent, Qt::WindowFlags f) :
QWidget(parent, f),
m_value(0),
m_flags(0),
m_invertValue(false),
m_showLabel(false),
m_labelText(""),
m_type(RADIO_WIDGET_NONE)
{
init();
}
RadioWidget::RadioWidget(const QString & labelText, int value, QWidget * parent, Qt::WindowFlags f) :
QWidget(parent, f),
m_value(value),
m_flags(0),
m_invertValue(false),
m_showLabel(true),
m_labelText(labelText),
m_type(RADIO_WIDGET_NONE)
{
init();
}
void RadioWidget::setIndex(int index)
{
m_index = index;
}
void RadioWidget::setInvertValue(bool invertValue)
{
m_invertValue = invertValue;
}
void RadioWidget::setValue(int value)
{
if (value != m_value) {
m_value = value;
emit valueChanged(m_value);
}
}
void RadioWidget::setFlags(quint16 flags)
{
if (flags != m_flags) {
m_flags = flags;
emit flagsChanged(flags);
}
}
void RadioWidget::setShowLabel(bool show)
{
if (show != m_showLabel) {
m_showLabel = show;
addLabel();
}
}
void RadioWidget::setLabelText(const QString & labelText, bool showLabel)
{
m_labelText = labelText;
m_showLabel = showLabel;
addLabel();
}
void RadioWidget::setStateData(const QByteArray & data)
{
RadioWidgetState state;
QDataStream stream(data);
stream >> state;
//setIndex(state.index);
setValue(state.value);
setFlags(state.flags);
}
void RadioWidget::changeVisibility(bool visible)
{
static QSizePolicy oldSP = sizePolicy();
setVisible(visible);
if (visible) {
setSizePolicy(oldSP);
}
else {
oldSP = sizePolicy();
setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
}
}
int RadioWidget::getValue() const
{
return m_value * (m_invertValue ? -1 : 1);
}
int RadioWidget::getIndex() const
{
return m_index;
}
int RadioWidget::getType() const
{
return m_type;
}
QByteArray RadioWidget::getStateData() const
{
QByteArray ba;
QDataStream stream(&ba, QIODevice::WriteOnly);
stream << getState();
return ba;
}
RadioWidget::RadioWidgetState RadioWidget::getState() const
{
return RadioWidgetState(quint8(m_type), qint8(m_index), qint16(m_value));;
}
void RadioWidget::init()
{
setIndex(0);
m_controlWidget = NULL;
m_nameLabel = NULL;
m_gridLayout= new QGridLayout(this);
m_gridLayout->setContentsMargins(0, 0, 0, 0);
m_gridLayout->setVerticalSpacing(4);
m_gridLayout->setHorizontalSpacing(0);
addLabel();
}
void RadioWidget::addLabel()
{
if (m_nameLabel) {
m_gridLayout->removeWidget(m_nameLabel);
m_nameLabel->deleteLater();
}
if (m_showLabel && !m_labelText.isEmpty()) {
m_nameLabel = new QLabel(m_labelText, this);
m_nameLabel->setAlignment(Qt::AlignCenter);
m_nameLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_gridLayout->addWidget(m_nameLabel, 1, 0, 1, 1, Qt::AlignTop);
m_gridLayout->setRowStretch(1, 0);
}
}
void RadioWidget::setWidget(QWidget * widget)
{
if (m_controlWidget) {
m_gridLayout->removeWidget(m_controlWidget);
m_controlWidget->deleteLater();
}
m_controlWidget = widget;
if (widget) {
m_gridLayout->addWidget(widget, 0, 0, 1, 1, Qt::AlignHCenter);
m_gridLayout->setRowStretch(0, 1);
}
}
QDataStream & operator << (QDataStream &out, const RadioWidget::RadioWidgetState & o)
{
out << o._version << o.type << o.index << o.value << o.flags;
return out;
}
QDataStream & operator >> (QDataStream &in, RadioWidget::RadioWidgetState & o)
{
if (o._version <= RADIO_WIDGET_STATE_VERSION)
in >> o._version >> o.type >> o.index >> o.value >> o.flags;
return in;
}
QDebug operator << (QDebug d, const RadioWidget::RadioWidgetState & o)
{
QDebugStateSaver saver(d);
d << "RadioWidget::RadioWidgetState: type=" << o.type << "; index=" << o.index
<< "; value=" << o.value << "; flags=0x" << hex << o.flags;
return d;
}
+53 -106
View File
@@ -21,146 +21,93 @@
#ifndef _RADIOWIDGET_H_
#define _RADIOWIDGET_H_
#include <QWidget>
#include <QLabel>
#include <QGridLayout>
#include <QDebug>
#include <QLabel>
#include <QWidget>
#define RADIO_WIDGET_STATE_VERSION 1
class RadioWidget : public QWidget
{
Q_OBJECT
public:
enum RadioWidgetType { RADIO_WIDGET_NONE, RADIO_WIDGET_SWITCH, RADIO_WIDGET_KNOB, RADIO_WIDGET_FADER };
explicit RadioWidget(QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()) :
QWidget(parent, f),
m_value(0),
m_invertValue(false),
m_showLabel(false),
m_labelText(""),
m_type(RADIO_WIDGET_NONE)
{
init();
}
explicit RadioWidget(const QString &labelText, int value = 0, QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()) :
QWidget(parent, f),
m_value(value),
m_invertValue(false),
m_showLabel(true),
m_labelText(labelText),
m_type(RADIO_WIDGET_NONE)
{
init();
}
// These are saved to persistent user settings in RadioWidgetState,
// so only append entires here, do not change index value if it can be avoided.
enum RadioWidgetType {
RADIO_WIDGET_NONE = 0,
RADIO_WIDGET_SWITCH,
RADIO_WIDGET_KNOB,
RADIO_WIDGET_FADER,
RADIO_WIDGET_STICK // actually one axis of a stick
};
void setIndex(int index) { m_index = index; }
void setInvertValue(bool invertValue) { m_invertValue = invertValue; }
struct RadioWidgetState {
public:
RadioWidgetState(quint8 type = 0, qint8 index = 0, qint16 value = 0, quint16 flags = 0) :
type(type), index(index), value(value), flags(flags),
_version(RADIO_WIDGET_STATE_VERSION)
{ }
void setValue(int value)
{
if (value != m_value) {
m_value = value;
emit valueChanged(m_value);
}
}
quint8 type;
qint8 index;
qint16 value;
quint16 flags;
void setShowLabel(bool show)
{
if (show != m_showLabel) {
m_showLabel = show;
addLabel();
}
}
friend QDataStream & operator << (QDataStream &out, const RadioWidgetState & o);
friend QDataStream & operator >> (QDataStream &in, RadioWidgetState & o);
friend QDebug operator << (QDebug d, const RadioWidgetState & o);
void setLabelText(const QString &labelText, bool showLabel = true)
{
m_labelText = labelText;
m_showLabel = showLabel;
addLabel();
}
private:
quint8 _version = RADIO_WIDGET_STATE_VERSION; // structure definition version
};
void changeVisibility(bool visible)
{
static QSizePolicy oldSP = sizePolicy();
setVisible(visible);
if (visible) {
setSizePolicy(oldSP);
}
else {
oldSP = sizePolicy();
setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
}
}
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());
int getValue() const { return m_value * (m_invertValue ? -1 : 1); }
int getIndex() const { return m_index; }
int getType() const { return m_type; }
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);
int getValue() const;
int getIndex() const;
int getType() const;
QByteArray getStateData() const;
RadioWidgetState getState() const;
protected:
void init()
{
setIndex(0);
m_controlWidget = NULL;
m_nameLabel = NULL;
m_gridLayout= new QGridLayout(this);
m_gridLayout->setContentsMargins(0, 0, 0, 0);
m_gridLayout->setVerticalSpacing(4);
m_gridLayout->setHorizontalSpacing(0);
addLabel();
}
void addLabel()
{
if (m_nameLabel) {
m_gridLayout->removeWidget(m_nameLabel);
m_nameLabel->deleteLater();
}
if (m_showLabel && !m_labelText.isEmpty()) {
m_nameLabel = new QLabel(m_labelText, this);
m_nameLabel->setAlignment(Qt::AlignCenter);
m_nameLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
m_gridLayout->addWidget(m_nameLabel, 1, 0, 1, 1, Qt::AlignTop);
m_gridLayout->setRowStretch(1, 0);
}
}
void setWidget(QWidget * widget = NULL)
{
if (m_controlWidget) {
m_gridLayout->removeWidget(m_controlWidget);
m_controlWidget->deleteLater();
}
m_controlWidget = widget;
if (widget) {
m_gridLayout->addWidget(widget, 0, 0, 1, 1, Qt::AlignHCenter);
m_gridLayout->setRowStretch(0, 1);
}
}
void init();
void addLabel();
void setWidget(QWidget * widget = NULL);
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;
QWidget * m_controlWidget;
QLabel * m_nameLabel;
signals:
void valueChanged(int m_value);
void flagsChanged(quint16 flags);
};
#endif // _RADIOWIDGET_H_
+69 -284
View File
@@ -26,9 +26,6 @@
#include <QDebug>
#include <QTextStream>
#include <QDialog>
#include <QComboBox>
#include <QLineEdit>
#include <QToolButton>
#if defined(JOYSTICKS) || defined(SIMU_AUDIO)
#include <SDL.h>
#undef main
@@ -37,7 +34,9 @@
#include "appdata.h"
#include "constants.h"
#include "eeprominterface.h"
#include "simulator.h"
#include "simulatordialog.h"
#include "simulatorstartupdialog.h"
#include "storage.h"
#include "qxtcommandoptions.h"
@@ -66,17 +65,7 @@ class MyProxyStyle : public QProxyStyle
};
#endif
typedef struct
{
int profileId;
QString firmwareId;
QString eepromFileName;
QString dataFolder;
bool useFolder;
} simulatorOptions_t;
QDir g_eepromDirectory;
using namespace Simulator;
int finish(int exitCode);
@@ -88,202 +77,6 @@ void showMessage(const QString & message, enum QMessageBox::Icon icon = QMessage
msgBox.exec();
}
bool usesCategorizedStorage(const QString &name)
{
return name.contains("horus", Qt::CaseInsensitive);
}
QString radioEepromFileName(QString firmwareId)
{
QString eepromFileName = "", ext = "bin";
QString radioId = firmwareId;
int pos = firmwareId.indexOf("-");
if (pos > 0) {
radioId = firmwareId.mid(pos+1);
pos = radioId.lastIndexOf("-");
if (pos > 0) {
radioId = radioId.mid(0, pos);
}
}
if (usesCategorizedStorage(radioId))
ext = "otx";
eepromFileName = QString("eeprom-%1.%2").arg(radioId, ext);
eepromFileName = g_eepromDirectory.filePath(eepromFileName.toLatin1());
// qDebug() << "radioId" << radioId << "eepromFileName" << eepromFileName;
return eepromFileName;
}
bool startupOptionsDialog(simulatorOptions_t &opts)
{
bool ret = false;
QString label;
QDialog * dialog = new QDialog();
dialog->setWindowFlags(dialog->windowFlags() & (~ Qt::WindowContextHelpButtonHint));
QFormLayout * form = new QFormLayout(dialog);
form->addRow(new QLabel(QObject::tr("Simulator Startup Options:")));
label = QObject::tr("Profile:");
QComboBox * cbProf = new QComboBox();
cbProf->setToolTip(QObject::tr("Existing radio profiles are shown here.<br/>" \
"Create or edit profiles using the Companion application."));
QMapIterator<int, QString> pi(g.getActiveProfiles());
while (pi.hasNext()) {
pi.next();
cbProf->addItem(pi.value(), pi.key());
if (pi.key() == opts.profileId)
cbProf->setCurrentIndex(cbProf->count() - 1);
}
form->addRow(label, cbProf);
label = QObject::tr("Radio Type:");
QComboBox * cbType = new QComboBox();
cbType->setToolTip(QObject::tr("Existing radio simulators are shown here.<br/>" \
"The radio type specified in the selected profile is used by default."));
cbType->addItems(registered_simulators.keys());
cbType->setCurrentIndex(cbType->findText(opts.firmwareId));
if (cbType->currentIndex() == -1 && cbProf->currentIndex() > -1){
SimulatorFactory * sf = getSimulatorFactory(g.profile[cbProf->currentIndex()].fwType());
if (sf)
cbType->setCurrentIndex(cbType->findText(sf->name()));
}
form->addRow(label, cbType);
label = QObject::tr("Data File:");
QLineEdit * fwFile = new QLineEdit(opts.eepromFileName, dialog);
fwFile->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
fwFile->setToolTip(QObject::tr("Radio data (.bin/.eeprom/.otx) image file to use. A new file with a default image will be created if necessary.<br />" \
"<b>NOTE</b>: any existing EEPROM data incompatible with the selected radio type may be overwritten!"));
QToolButton * fwBtn = new QToolButton(dialog);
fwBtn->setText("...");
fwBtn->setToolButtonStyle(Qt::ToolButtonTextOnly);
fwBtn->setToolTip(QObject::tr("Select radio data..."));
QWidget * fw = new QWidget(dialog);
QHBoxLayout * hl = new QHBoxLayout(fw);
hl->setContentsMargins(0, 0, 0, 0);
hl->setSpacing(2);
hl->addWidget(fwFile, 2);
hl->addWidget(fwBtn, 0);
form->addRow(label, fw);
label = QObject::tr("Data Folder:");
QLineEdit * fwFolder = new QLineEdit(opts.dataFolder, dialog);
fwFolder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
fwFolder->setToolTip(QObject::tr("Directory containing RADIO and MODELS folders to use." \
"<br>New folder(s) with default radio/model will be created here if necessary."));
QToolButton * fwFolderBtn = new QToolButton(dialog);
fwFolderBtn->setText("...");
fwFolderBtn->setToolButtonStyle(Qt::ToolButtonTextOnly);
fwFolderBtn->setToolTip(QObject::tr("Select data folder..."));
QWidget * fwf = new QWidget(dialog);
QHBoxLayout * hlf = new QHBoxLayout(fwf);
hlf->setContentsMargins(0, 0, 0, 0);
hlf->setSpacing(2);
hlf->addWidget(fwFolder, 2);
hlf->addWidget(fwFolderBtn, 0);
form->addRow(label, fwf);
label = QObject::tr("Date Source:");
QRadioButton * dsfileBtn = new QRadioButton(QObject::tr("File"), dialog);
QRadioButton * dsdirBtn = new QRadioButton(QObject::tr("Folder"), dialog);
dsfileBtn->setChecked(true);
QButtonGroup * btngrp = new QButtonGroup(dialog);
btngrp->setExclusive(true);
btngrp->addButton(dsfileBtn);
btngrp->addButton(dsdirBtn);
QWidget * btnwdgt = new QWidget(dialog);
QHBoxLayout * btnlo = new QHBoxLayout(btnwdgt);
btnlo->setContentsMargins(0, 0, 0, 0);
btnlo->setSpacing(9);
btnlo->addWidget(dsfileBtn);
btnlo->addWidget(dsdirBtn);
btnlo->addStretch(1);
form->addRow(label, btnwdgt);
QDialogButtonBox buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, dialog);
form->addRow(&buttonBox);
dialog->resize(400, dialog->sizeHint().height());
QObject::connect(&buttonBox, SIGNAL(accepted()), dialog, SLOT(accept()));
QObject::connect(&buttonBox, SIGNAL(rejected()), dialog, SLOT(reject()));
auto changeContainerType = [cbType, btnwdgt, dsfileBtn, dsdirBtn, fw, fwf, form] (void)
{
bool show = usesCategorizedStorage(cbType->currentText());
//btnwdgt->setVisible(show);
//form->labelForField(btnwdgt)->setVisible(show);
fwf->setVisible(show);
form->labelForField(fwf)->setVisible(show);
// TODO : remove below once .otx support works and un-comment 2 lines above
fw->setVisible(!show);
form->labelForField(fw)->setVisible(!show);
dsdirBtn->setChecked(show);
dsfileBtn->setChecked(!show);
btnwdgt->setVisible(false);
form->labelForField(btnwdgt)->setVisible(false);
};
changeContainerType();
// set new default radio type when profile choice changes
QObject::connect(cbProf, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [cbProf, cbType] (int index) {
if (index < 0)
return;
SimulatorFactory * sf = getSimulatorFactory(g.profile[index].fwType());
if (sf) {
int i = cbType->findText(sf->name());
if (i > -1) {
cbType->setCurrentIndex(i);
}
}
});
// set new default firmware file when radio type changes
QObject::connect(cbType, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [cbType, fwFile, changeContainerType] (int index) {
if (index < 0)
return;
fwFile->setText(radioEepromFileName(cbType->currentText()));
changeContainerType();
});
// connect button to file selector dialog
QObject::connect(fwBtn, &QToolButton::clicked, [dialog, fwFile, cbType, opts, dsfileBtn] (bool) {
QString filter = QObject::tr((usesCategorizedStorage(cbType->currentText()) ? OTX_FILES_FILTER : EEPROM_FILES_FILTER));
filter += QObject::tr("All files (*.*)");
QString file = QFileDialog::getSaveFileName(dialog, QObject::tr("Select EEPROM image"), fwFile->text(),
filter, NULL, QFileDialog::DontConfirmOverwrite);
if (!file.isEmpty()) {
fwFile->setText(file);
dsfileBtn->setChecked(true);
}
});
// connect button to folder selector dialog
QObject::connect(fwFolderBtn, &QToolButton::clicked, [dialog, fwFolder, cbType, opts, dsdirBtn](bool) {
QString folder = QFileDialog::getExistingDirectory(dialog, QObject::tr("Select Data Directory"), fwFolder->text(), QFileDialog::DontUseNativeDialog);
if (!folder.isEmpty()) {
fwFolder->setText(folder);
dsdirBtn->setChecked(true);
}
});
// go
if (dialog->exec() == QDialog::Accepted) {
opts.profileId = cbProf->currentData().toInt();
opts.firmwareId = cbType->currentText();
opts.eepromFileName = fwFile->text();
opts.dataFolder = fwFolder->text();
opts.useFolder = dsdirBtn->isChecked();
ret = true;
}
dialog->deleteLater();
return ret;
}
void sharedHelpText(QTextStream &stream)
{
// list all available profiles
@@ -300,24 +93,36 @@ void sharedHelpText(QTextStream &stream)
}
}
QString findFirmwareId(int profileId)
{
SimulatorFactory * sf = getSimulatorFactory(g.profile[profileId].fwType());
if (sf)
return sf->name();
else
return QString();
}
int main(int argc, char *argv[])
{
Q_INIT_RESOURCE(companion);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
#if defined(WIN32) && defined(WIN_USE_CONSOLE_STDIO)
AllocConsole();
SetConsoleTitle("Simulator Console");
freopen("conin$", "r", stdin);
freopen("conout$", "w", stdout);
freopen("conout$", "w", stderr);
if (!GetConsoleWindow()) {
AllocConsole();
freopen("conin$", "r", stdin);
freopen("conout$", "w", stdout);
freopen("conout$", "w", stderr);
}
SetConsoleTitle(APP_SIMULATOR " Console");
#endif
QApplication app(argc, argv);
app.setApplicationName("OpenTX Simulator");
app.setOrganizationName("OpenTX");
app.setOrganizationDomain("open-tx.org");
app.setApplicationName(APP_SIMULATOR);
app.setOrganizationName(COMPANY);
app.setOrganizationDomain(COMPANY_DOMAIN);
g.init();
@@ -348,20 +153,14 @@ int main(int argc, char *argv[])
}
#endif
simulatorOptions_t simOptions;
QxtCommandOptions cliOptions;
bool cliOptsFound = false;
int profileId = (g.simuLastProfId() > -1 ? g.simuLastProfId() : g.id());
SimulatorOptions simOptions = g.profile[profileId].simulatorOptions();
g_eepromDirectory = QDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation));
if (!g_eepromDirectory.exists("OpenTX")) {
if (!g_eepromDirectory.mkpath("OpenTX")) {
showMessage(QObject::tr("WARNING: couldn't create directory for EEPROM:\n%1").arg(g_eepromDirectory.absoluteFilePath("OpenTX")), QMessageBox::Warning);
}
}
g_eepromDirectory.cd("OpenTX");
registerSimulators();
registerStorageFactories();
registerOpenTxFirmwares();
registerSimulators();
if (!registered_simulators.size()) {
showMessage(QObject::tr("ERROR: No simulator libraries available."), QMessageBox::Critical);
@@ -387,75 +186,71 @@ int main(int argc, char *argv[])
return finish(1);
}
if (cliOptions.count("radio") == 1) {
simOptions.firmwareId = cliOptions.value("radio").toString();
cliOptsFound = true;
}
else {
simOptions.firmwareId = g.lastSimulator();
}
// TODO : defaults should be set in Profile::init()
if (simOptions.firmwareId.isEmpty())
simOptions.firmwareId = findFirmwareId(profileId);
if (simOptions.dataFolder.isEmpty())
simOptions.dataFolder = g.eepromDir();
if (simOptions.sdPath.isEmpty())
simOptions.sdPath = g.profile[profileId].sdPath();
if (cliOptions.count("profile") == 1) {
bool chk;
int pid = cliOptions.value("profile").toInt(&chk);
if (chk) {
simOptions.profileId = pid;
}
else {
simOptions.profileId = g.getActiveProfiles().key(cliOptions.value("profile").toString(), -1);
}
// load default radio for this profile if not already passed on command line
if (!cliOptsFound) {
SimulatorFactory * sf = getSimulatorFactory(g.profile[simOptions.profileId].fwType());
if (sf)
simOptions.firmwareId = sf->name();
if (chk)
profileId = pid;
else
profileId = g.getActiveProfiles().key(cliOptions.value("profile").toString(), -1);
if (!g.getActiveProfiles().contains(profileId)) {
fprintf(stderr, "Error: Profile ID %d was not found.", profileId);
return finish(1);
}
simOptions = g.profile[profileId].simulatorOptions();
cliOptsFound = true;
}
else if (g.simuLastProfId() != -1) {
simOptions.profileId = g.simuLastProfId();
}
else {
simOptions.profileId = g.id();
if (cliOptions.count("radio") == 1) {
simOptions.firmwareId = cliOptions.value("radio").toString();
cliOptsFound = true;
}
if (cliOptsFound || (simOptions.dataFile.isEmpty() && !simOptions.firmwareId.isEmpty()))
simOptions.dataFile = SimulatorStartupDialog::radioEepromFileName(simOptions.firmwareId, g.eepromDir());
if (!cliOptions.positional().isEmpty()) {
if (QString(cliOptions.positional().at(0)).contains(QRegExp(".*\\.[\\w]{2,6}$"))) {
simOptions.eepromFileName = cliOptions.positional()[0];
simOptions.dataFile = cliOptions.positional()[0];
simOptions.startupDataType = SimulatorOptions::START_WITH_FILE;
}
else {
simOptions.dataFolder = cliOptions.positional()[0];
simOptions.useFolder = true;
simOptions.startupDataType = SimulatorOptions::START_WITH_FOLDER;
}
cliOptsFound = true;
}
else if (cliOptsFound || g.simuLastEepe().isEmpty()) {
simOptions.eepromFileName = radioEepromFileName(simOptions.firmwareId);
simOptions.dataFolder = g_eepromDirectory.absolutePath();
}
else {
simOptions.eepromFileName = g.simuLastEepe();
simOptions.dataFolder = g.simuLastFolder();
}
if (!cliOptsFound || simOptions.profileId == -1 || simOptions.firmwareId.isEmpty() || (simOptions.eepromFileName.isEmpty() && simOptions.dataFolder.isEmpty())) {
if (!startupOptionsDialog(simOptions)) {
if (!cliOptsFound || profileId == -1 || simOptions.firmwareId.isEmpty() || (simOptions.dataFile.isEmpty() && simOptions.dataFolder.isEmpty())) {
SimulatorStartupDialog * dlg = new SimulatorStartupDialog(&simOptions, &profileId);
int ret = dlg->exec();
dlg->deleteLater();
if (ret != QDialog::Accepted) {
return finish(0);
}
}
qDebug() << "firmwareId" << simOptions.firmwareId << "profileId" << simOptions.profileId << "eepromFileName" \
<< simOptions.eepromFileName << "dataFolder" << simOptions.dataFolder << "useFolder" << simOptions.useFolder;
qDebug() << __FILE__ << __LINE__ << "profileId=" << profileId << simOptions;
if (simOptions.profileId < 0 || simOptions.firmwareId.isEmpty() || (simOptions.eepromFileName.isEmpty() && simOptions.dataFolder.isEmpty())) {
showMessage(QObject::tr("ERROR: Couldn't start simulator, missing radio/profile/data file/folder.\nProfile ID: [%1]; Radio ID: [%2];\nData File: [%3]")
.arg(simOptions.profileId).arg(simOptions.firmwareId).arg(simOptions.eepromFileName), QMessageBox::Critical);
if (profileId < 0 || simOptions.firmwareId.isEmpty() || (simOptions.dataFile.isEmpty() && simOptions.dataFolder.isEmpty())) {
showMessage(QObject::tr("ERROR: Couldn't start simulator, missing radio/profile/data file/folder.\n Profile ID: [%1]; Radio ID: [%2];\nData File: [%3]")
.arg(profileId).arg(simOptions.firmwareId, simOptions.dataFile), QMessageBox::Critical);
return finish(1);
}
if (!g.getActiveProfiles().contains(simOptions.profileId) || !registered_simulators.keys().contains(simOptions.firmwareId)) {
if (!g.getActiveProfiles().contains(profileId) || !registered_simulators.keys().contains(simOptions.firmwareId)) {
QString msg;
QTextStream stream(&msg);
stream << QObject::tr("ERROR: Radio profile or simulator firmware not found.\nProfile ID: [%1]; Radio ID: [%2]")
.arg(simOptions.profileId).arg(simOptions.firmwareId);
.arg(profileId).arg(simOptions.firmwareId);
sharedHelpText(stream);
showMessage(msg, QMessageBox::Critical);
return finish(2);
@@ -468,24 +263,14 @@ int main(int argc, char *argv[])
}
current_firmware_variant = getFirmware(simOptions.firmwareId);
registerStorageFactories();
int oldProfId = g.id();
g.id(simOptions.profileId);
g.simuLastProfId(simOptions.profileId);
g.lastSimulator(simOptions.firmwareId);
g.simuLastEepe(simOptions.eepromFileName);
g.simuLastFolder(simOptions.dataFolder);
g.id(profileId);
g.simuLastProfId(profileId);
SimulatorDialog * dialog = new SimulatorDialog(NULL, factory->create(), SIMULATOR_FLAGS_STANDALONE);
dialog->setRadioProfileId(simOptions.profileId);
if (simOptions.useFolder && !simOptions.dataFolder.isEmpty()) {
dialog->setDataPath(simOptions.dataFolder);
dialog->setEepromData();
}
else {
dialog->setEepromData(simOptions.eepromFileName.toLocal8Bit(), true);
}
dialog->setRadioProfileId(profileId);
dialog->setOptions(simOptions, true);
dialog->start();
dialog->show();
int result = app.exec();
+146 -244
View File
@@ -24,191 +24,93 @@
AppData g;
// ** CompStoreObj class********************
void CompStoreObj::clear (const QString tag1, const QString tag2, const QString tag3)
void CompStoreObj::clear (const QString &tag1, const QString &tag2, const QString &tag3)
{
QSettings settings(COMPANY, PRODUCT);
if (tag2.isEmpty())
{
settings.remove(tag1);
}
else if (tag3.isEmpty())
{
settings.beginGroup(tag1);
settings.remove(tag2);
settings.endGroup();
}
else
{
settings.beginGroup(tag1);
settings.beginGroup(tag2);
settings.remove(tag3);
settings.endGroup();
settings.endGroup();
}
QSettings settings(COMPANY, PRODUCT);
if (tag2.isEmpty())
{
settings.remove(tag1);
}
else if (tag3.isEmpty())
{
settings.beginGroup(tag1);
settings.remove(tag2);
settings.endGroup();
}
else
{
settings.beginGroup(tag1);
settings.beginGroup(tag2);
settings.remove(tag3);
settings.endGroup();
settings.endGroup();
}
}
void CompStoreObj::store(const QByteArray newArray, QByteArray &array, const QString tag, const QString group1, const QString group2 )
template <typename OBJ_T, typename TAG_T, typename GP1_T, typename GP2_T>
void CompStoreObj::store(const OBJ_T newValue, OBJ_T & destValue, const TAG_T tag, const GP1_T group1, const GP2_T group2)
{
QSettings settings(COMPANY, PRODUCT);
if (!group1.isEmpty()) settings.beginGroup(group1);
if (!group2.isEmpty()) settings.beginGroup(group2);
QSettings settings(COMPANY, PRODUCT);
QString g1 = group1; // these may come in as const char * or QString
QString g2 = group2;
if (!g1.isEmpty()){
settings.beginGroup(g1);
if (!g2.isEmpty())
settings.beginGroup(g2);
}
settings.setValue(tag, newArray);
array = newArray;
settings.setValue(tag, QVariant::fromValue(newValue));
destValue = newValue;
if (!group1.isEmpty()) settings.endGroup();
if (!group2.isEmpty()) settings.endGroup();
if (!g1.isEmpty()){
settings.endGroup();
if (!g2.isEmpty())
settings.endGroup();
}
}
void CompStoreObj::store(const QStringList newSList, QStringList &stringList, const QString tag, const QString group1, const QString group2 )
template <typename OBJ_T, typename TAG_T, typename DEF_T, typename GP1_T, typename GP2_T>
void CompStoreObj::retrieve(OBJ_T & destValue, const TAG_T tag, const DEF_T def, const GP1_T group1, const GP2_T group2)
{
QSettings settings(COMPANY, PRODUCT);
if (!group1.isEmpty()) settings.beginGroup(group1);
if (!group2.isEmpty()) settings.beginGroup(group2);
QSettings settings(COMPANY, PRODUCT);
QString g1 = group1; // these may come in as const char * or QString
QString g2 = group2;
if (!g1.isEmpty()){
settings.beginGroup(g1);
if (!g2.isEmpty())
settings.beginGroup(g2);
}
settings.setValue(tag, newSList);
stringList = newSList;
QVariant val = settings.value(QString(tag), QVariant::fromValue(def));
if (val.canConvert<OBJ_T>())
destValue = val.value<OBJ_T>();
if (!group1.isEmpty()) settings.endGroup();
if (!group2.isEmpty()) settings.endGroup();
if (!g1.isEmpty()){;
settings.endGroup();
if (!g2.isEmpty())
settings.endGroup();
}
}
void CompStoreObj::store(const QString newString, QString &string, const QString tag, const QString group1, const QString group2 )
template <typename OBJ_T, typename TAG_T, typename DEF_T, typename GP1_T, typename GP2_T>
void CompStoreObj::getset(OBJ_T & value, const TAG_T tag, const DEF_T def, const GP1_T group1, const GP2_T group2 )
{
QSettings settings(COMPANY, PRODUCT);
if (!group1.isEmpty()) settings.beginGroup(group1);
if (!group2.isEmpty()) settings.beginGroup(group2);
settings.setValue(tag, newString);
string = newString;
if (!group1.isEmpty()) settings.endGroup();
if (!group2.isEmpty()) settings.endGroup();
retrieve(value, tag, def, group1, group2);
store(value, value, tag, group1, group2);
}
void CompStoreObj::store(const bool newTruth, bool &truth, const QString tag, const QString group1, const QString group2 )
template <typename OBJ_T, typename TAG_T, typename GP1_T, typename GP2_T>
void CompStoreObj::getset(OBJ_T & value, const TAG_T tag, const char * def, const GP1_T group1, const GP2_T group2 )
{
QSettings settings(COMPANY, PRODUCT);
if (!group1.isEmpty()) settings.beginGroup(group1);
if (!group2.isEmpty()) settings.beginGroup(group2);
settings.setValue(tag, newTruth);
truth = newTruth;
if (!group1.isEmpty()) settings.endGroup();
if (!group2.isEmpty()) settings.endGroup();
getset(value, tag, QString(def), group1, group2);
}
void CompStoreObj::store(const int newNumber, int &number, const QString tag, const QString group1, const QString group2 )
{
QSettings settings(COMPANY, PRODUCT);
if (!group1.isEmpty()) settings.beginGroup(group1);
if (!group2.isEmpty()) settings.beginGroup(group2);
settings.setValue(tag, newNumber);
number = newNumber;
if (!group1.isEmpty()) settings.endGroup();
if (!group2.isEmpty()) settings.endGroup();
}
// Retrieval functions
void CompStoreObj::retrieve( QByteArray &array, const QString tag, const QString def, const QString group1, const QString group2 )
{
QSettings settings(COMPANY, PRODUCT);
if (!group1.isEmpty()) settings.beginGroup(group1);
if (!group2.isEmpty()) settings.beginGroup(group2);
array = settings.value(tag, def).toByteArray();
if (!group1.isEmpty()) settings.endGroup();
if (!group2.isEmpty()) settings.endGroup();
}
void CompStoreObj::retrieve( QStringList &stringList, const QString tag, const QString def, const QString group1, const QString group2 )
{
QSettings settings(COMPANY, PRODUCT);
if (!group1.isEmpty()) settings.beginGroup(group1);
if (!group2.isEmpty()) settings.beginGroup(group2);
stringList = settings.value(tag, def).toStringList();
if (!group1.isEmpty()) settings.endGroup();
if (!group2.isEmpty()) settings.endGroup();
}
void CompStoreObj::retrieve( QString &string, const QString tag, const QString def, const QString group1, const QString group2 )
{
QSettings settings(COMPANY, PRODUCT);
if (!group1.isEmpty()) settings.beginGroup(group1);
if (!group2.isEmpty()) settings.beginGroup(group2);
string = settings.value(tag, def).toString();
if (!group1.isEmpty()) settings.endGroup();
if (!group2.isEmpty()) settings.endGroup();
}
void CompStoreObj::retrieve( bool &truth, const QString tag, const bool def, const QString group1, const QString group2 )
{
QSettings settings(COMPANY, PRODUCT);
if (!group1.isEmpty()) settings.beginGroup(group1);
if (!group2.isEmpty()) settings.beginGroup(group2);
truth = settings.value(tag, def).toBool();
if (!group1.isEmpty()) settings.endGroup();
if (!group2.isEmpty()) settings.endGroup();
}
void CompStoreObj::retrieve( int &number, const QString tag, const int def, const QString group1, const QString group2 )
{
QSettings settings(COMPANY, PRODUCT);
if (!group1.isEmpty()) settings.beginGroup(group1);
if (!group2.isEmpty()) settings.beginGroup(group2);
number = settings.value(tag, def).toInt();
if (!group1.isEmpty()) settings.endGroup();
if (!group2.isEmpty()) settings.endGroup();
}
// Retrieve and Store functions
void CompStoreObj::getset( QByteArray &array, const QString tag, const QString def, const QString group1, const QString group2 )
{
retrieve( array, tag, def, group1, group2);
store(array, array, tag, group1, group2);
}
void CompStoreObj::getset( QStringList &stringList, const QString tag, const QString def, const QString group1, const QString group2 )
{
retrieve( stringList, tag, def, group1, group2);
store(stringList, stringList, tag, group1, group2);
}
void CompStoreObj::getset( QString &string, const QString tag, const QString def, const QString group1, const QString group2 )
{
retrieve( string, tag, def, group1, group2);
store(string, string, tag, group1, group2);
}
void CompStoreObj::getset( bool &truth, const QString tag, const bool def, const QString group1, const QString group2 )
{
retrieve( truth, tag, def, group1, group2);
store(truth, truth, tag, group1, group2);
}
void CompStoreObj::getset( int &number, const QString tag, const int def, const QString group1, const QString group2 )
{
retrieve( number, tag, def, group1, group2);
store(number, number, tag, group1, group2);
}
// ** FwRevision class********************
int FwRevision::get( const QString fwType )
{
QString result;
retrieve( result, fwType, "", "FwRevisions" );
retrieve( result, fwType, QString(""), "FwRevisions" );
return result.toInt();
}
@@ -310,8 +212,6 @@ bool Profile::renameFwFiles() const { return _renameFwFiles; }
int Profile::channelOrder() const { return _channelOrder; }
int Profile::defaultMode() const { return _defaultMode; }
QByteArray Profile::simuWinGeo() const { return _simuWinGeo; }
QString Profile::beeper() const { return _beeper; }
QString Profile::countryCode() const { return _countryCode; }
QString Profile::display() const { return _display; }
@@ -322,47 +222,48 @@ QString Profile::timeStamp() const { return _timeStamp; }
QString Profile::trainerCalib() const { return _trainerCalib; }
QString Profile::controlTypes() const { return _controlTypes; }
QString Profile::controlNames() const { return _controlNames; }
int Profile::txCurrentCalibration() const { return _txCurrentCalibration; }
int Profile::gsStickMode() const { return _gsStickMode; }
int Profile::ppmMultiplier() const { return _ppmMultiplier; }
int Profile::txVoltageCalibration() const { return _txVoltageCalibration; }
int Profile::vBatWarn() const { return _vBatWarn; }
int Profile::vBatMin() const { return _vBatMin; }
int Profile::vBatMax() const { return _vBatMax; }
int Profile::txCurrentCalibration() const { return _txCurrentCalibration; }
int Profile::txVoltageCalibration() const { return _txVoltageCalibration; }
SimulatorOptions Profile::simulatorOptions() const { return _simulatorOptions; }
// Set declarations
void Profile::name (const QString x) { store(x, _name, "Name" ,"Profiles", QString("profile%1").arg(index));}
void Profile::fwName (const QString x) { store(x, _fwName, "fwName" ,"Profiles", QString("profile%1").arg(index));}
void Profile::fwType (const QString x) { store(x, _fwType, "fwType" ,"Profiles", QString("profile%1").arg(index));}
void Profile::sdPath (const QString x) { store(x, _sdPath, "sdPath" ,"Profiles", QString("profile%1").arg(index));}
void Profile::volumeGain (const int x) { store(x, _volumeGain, "volumeGain" ,"Profiles", QString("profile%1").arg(index));}
void Profile::pBackupDir (const QString x) { store(x, _pBackupDir, "pBackupDir" ,"Profiles", QString("profile%1").arg(index));}
void Profile::splashFile (const QString x) { store(x, _splashFile, "SplashFileName" ,"Profiles", QString("profile%1").arg(index));}
void Profile::burnFirmware (const bool x) { store(x, _burnFirmware, "burnFirmware" ,"Profiles", QString("profile%1").arg(index));}
void Profile::renameFwFiles (const bool x) { store(x, _renameFwFiles, "rename_firmware_files" ,"Profiles", QString("profile%1").arg(index));}
void Profile::penableBackup (const bool x) { store(x, _penableBackup, "penableBackup" ,"Profiles", QString("profile%1").arg(index));}
void Profile::channelOrder (const int x) { store(x, _channelOrder, "default_channel_order" ,"Profiles", QString("profile%1").arg(index));}
void Profile::defaultMode (const int x) { store(x, _defaultMode, "default_mode" ,"Profiles", QString("profile%1").arg(index));}
void Profile::name (const QString x) { store(x, _name, "Name" ,"Profiles", groupId());}
void Profile::fwName (const QString x) { store(x, _fwName, "fwName" ,"Profiles", groupId());}
void Profile::fwType (const QString x) { store(x, _fwType, "fwType" ,"Profiles", groupId());}
void Profile::sdPath (const QString x) { store(x, _sdPath, "sdPath" ,"Profiles", groupId());}
void Profile::volumeGain (const int x) { store(x, _volumeGain, "volumeGain" ,"Profiles", groupId());}
void Profile::pBackupDir (const QString x) { store(x, _pBackupDir, "pBackupDir" ,"Profiles", groupId());}
void Profile::splashFile (const QString x) { store(x, _splashFile, "SplashFileName" ,"Profiles", groupId());}
void Profile::burnFirmware (const bool x) { store(x, _burnFirmware, "burnFirmware" ,"Profiles", groupId());}
void Profile::renameFwFiles (const bool x) { store(x, _renameFwFiles, "rename_firmware_files" ,"Profiles", groupId());}
void Profile::penableBackup (const bool x) { store(x, _penableBackup, "penableBackup" ,"Profiles", groupId());}
void Profile::channelOrder (const int x) { store(x, _channelOrder, "default_channel_order" ,"Profiles", groupId());}
void Profile::defaultMode (const int x) { store(x, _defaultMode, "default_mode" ,"Profiles", groupId());}
void Profile::beeper (const QString x) { store(x, _beeper, "Beeper" ,"Profiles", groupId());}
void Profile::countryCode (const QString x) { store(x, _countryCode, "countryCode" ,"Profiles", groupId());}
void Profile::display (const QString x) { store(x, _display, "Display" ,"Profiles", groupId());}
void Profile::haptic (const QString x) { store(x, _haptic, "Haptic" ,"Profiles", groupId());}
void Profile::speaker (const QString x) { store(x, _speaker, "Speaker" ,"Profiles", groupId());}
void Profile::stickPotCalib (const QString x) { store(x, _stickPotCalib, "StickPotCalib" ,"Profiles", groupId());}
void Profile::timeStamp (const QString x) { store(x, _timeStamp, "TimeStamp" ,"Profiles", groupId());}
void Profile::trainerCalib (const QString x) { store(x, _trainerCalib, "TrainerCalib" ,"Profiles", groupId());}
void Profile::controlTypes (const QString x) { store(x, _controlTypes, "ControlTypes" ,"Profiles", groupId());}
void Profile::controlNames (const QString x) { store(x, _controlNames, "ControlNames" ,"Profiles", groupId());}
void Profile::gsStickMode (const int x) { store(x, _gsStickMode, "GSStickMode" ,"Profiles", groupId());}
void Profile::ppmMultiplier (const int x) { store(x, _ppmMultiplier, "PPM_Multiplier" ,"Profiles", groupId());}
void Profile::vBatWarn (const int x) { store(x, _vBatWarn, "vBatWarn" ,"Profiles", groupId());}
void Profile::vBatMin (const int x) { store(x, _vBatMin, "VbatMin" ,"Profiles", groupId());}
void Profile::vBatMax (const int x) { store(x, _vBatMax, "VbatMax" ,"Profiles", groupId());}
void Profile::txCurrentCalibration (const int x) { store(x, _txCurrentCalibration, "currentCalib","Profiles", groupId());}
void Profile::txVoltageCalibration (const int x) { store(x, _txVoltageCalibration, "VbatCalib" ,"Profiles", groupId());}
void Profile::simuWinGeo (const QByteArray x) { store(x, _simuWinGeo, "simuWindowGeometry" ,"Profiles", QString("profile%1").arg(index));}
void Profile::beeper (const QString x) { store(x, _beeper, "Beeper" ,"Profiles", QString("profile%1").arg(index));}
void Profile::countryCode (const QString x) { store(x, _countryCode, "countryCode" ,"Profiles", QString("profile%1").arg(index));}
void Profile::display (const QString x) { store(x, _display, "Display" ,"Profiles", QString("profile%1").arg(index));}
void Profile::haptic (const QString x) { store(x, _haptic, "Haptic" ,"Profiles", QString("profile%1").arg(index));}
void Profile::speaker (const QString x) { store(x, _speaker, "Speaker" ,"Profiles", QString("profile%1").arg(index));}
void Profile::stickPotCalib (const QString x) { store(x, _stickPotCalib, "StickPotCalib" ,"Profiles", QString("profile%1").arg(index));}
void Profile::timeStamp (const QString x) { store(x, _timeStamp, "TimeStamp" ,"Profiles", QString("profile%1").arg(index));}
void Profile::trainerCalib (const QString x) { store(x, _trainerCalib, "TrainerCalib" ,"Profiles", QString("profile%1").arg(index));}
void Profile::controlTypes (const QString x) { store(x, _controlTypes, "ControlTypes" ,"Profiles", QString("profile%1").arg(index));}
void Profile::controlNames (const QString x) { store(x, _controlNames, "ControlNames" ,"Profiles", QString("profile%1").arg(index));}
void Profile::txCurrentCalibration (const int x) { store(x, _txCurrentCalibration, "currentCalib","Profiles", QString("profile%1").arg(index));}
void Profile::gsStickMode (const int x) { store(x, _gsStickMode, "GSStickMode" ,"Profiles", QString("profile%1").arg(index));}
void Profile::ppmMultiplier (const int x) { store(x, _ppmMultiplier, "PPM_Multiplier" ,"Profiles", QString("profile%1").arg(index));}
void Profile::txVoltageCalibration (const int x) { store(x, _txVoltageCalibration, "VbatCalib","Profiles", QString("profile%1").arg(index));}
void Profile::vBatWarn (const int x) { store(x, _vBatWarn, "vBatWarn" ,"Profiles", QString("profile%1").arg(index));}
void Profile::vBatMin (const int x) { store(x, _vBatMin, "VbatMin" ,"Profiles", QString("profile%1").arg(index));}
void Profile::vBatMax (const int x) { store(x, _vBatMax, "VbatMax" ,"Profiles", QString("profile%1").arg(index));}
void Profile::simulatorOptions(const SimulatorOptions & x) { store(x, _simulatorOptions, "simulatorOptions" ,"Profiles", groupId()); }
// Constructor
Profile::Profile()
@@ -394,13 +295,14 @@ Profile& Profile::operator=(const Profile& rhs)
trainerCalib ( rhs.trainerCalib() );
controlTypes ( rhs.controlTypes() );
controlNames ( rhs.controlNames() );
txCurrentCalibration ( rhs.txCurrentCalibration() );
gsStickMode ( rhs.gsStickMode() );
ppmMultiplier( rhs.ppmMultiplier() );
txVoltageCalibration ( rhs.txVoltageCalibration() );
vBatWarn ( rhs.vBatWarn() );
vBatMin ( rhs.vBatMin() );
vBatMax ( rhs.vBatMax() );
txCurrentCalibration ( rhs.txCurrentCalibration() );
txVoltageCalibration ( rhs.txVoltageCalibration() );
simulatorOptions ( rhs.simulatorOptions() );
return *this;
}
@@ -410,7 +312,7 @@ void Profile::remove()
// Remove all profile values from settings file
QSettings settings(COMPANY, PRODUCT);
settings.beginGroup("Profiles");
settings.remove(QString("profile%1").arg(index));
settings.remove(groupId());
settings.endGroup();
// Reset all profile variables to initial values
@@ -421,7 +323,7 @@ bool Profile::existsOnDisk()
{
QSettings settings(COMPANY, PRODUCT);
settings.beginGroup("Profiles");
settings.beginGroup(QString("profile%1").arg(index));
settings.beginGroup(groupId());
QStringList keyList = settings.allKeys();
settings.endGroup();
settings.endGroup();
@@ -468,7 +370,7 @@ void Profile::init(int newIndex)
_channelOrder = 0;
_defaultMode = 1;
_simuWinGeo = QByteArray();
_simulatorOptions = SimulatorOptions();
initFwVariables();
@@ -482,38 +384,44 @@ void Profile::init(int newIndex)
void Profile::flush()
{
// Load and store all variables. Use default values if setting values are missing
getset( _fwName, "fwName" ,"" ,"Profiles", QString("profile%1").arg(index));
getset( _fwType, "fwType" ,"" ,"Profiles", QString("profile%1").arg(index));
getset( _name, "Name" ,"" ,"Profiles", QString("profile%1").arg(index));
getset( _sdPath, "sdPath" ,"" ,"Profiles", QString("profile%1").arg(index));
getset( _volumeGain, "volumeGain" ,10 ,"Profiles", QString("profile%1").arg(index));
getset( _pBackupDir, "pBackupDir" ,"" ,"Profiles", QString("profile%1").arg(index));
getset( _splashFile, "SplashFileName" ,"" ,"Profiles", QString("profile%1").arg(index));
getset( _burnFirmware, "burnFirmware" ,false ,"Profiles", QString("profile%1").arg(index));
getset( _penableBackup, "penableBackup" ,false ,"Profiles", QString("profile%1").arg(index));
getset( _renameFwFiles, "rename_firmware_files" ,false ,"Profiles", QString("profile%1").arg(index));
getset( _channelOrder, "default_channel_order" ,0 ,"Profiles", QString("profile%1").arg(index));
getset( _defaultMode, "default_mode" ,1 ,"Profiles", QString("profile%1").arg(index));
getset( _fwName, "fwName" ,"" ,"Profiles", groupId());
getset( _fwType, "fwType" ,"" ,"Profiles", groupId());
getset( _name, "Name" ,"" ,"Profiles", groupId());
getset( _sdPath, "sdPath" ,"" ,"Profiles", groupId());
getset( _volumeGain, "volumeGain" ,10 ,"Profiles", groupId());
getset( _pBackupDir, "pBackupDir" ,"" ,"Profiles", groupId());
getset( _splashFile, "SplashFileName" ,"" ,"Profiles", groupId());
getset( _burnFirmware, "burnFirmware" ,false ,"Profiles", groupId());
getset( _penableBackup, "penableBackup" ,false ,"Profiles", groupId());
getset( _renameFwFiles, "rename_firmware_files" ,false ,"Profiles", groupId());
getset( _channelOrder, "default_channel_order" ,0 ,"Profiles", groupId());
getset( _defaultMode, "default_mode" ,1 ,"Profiles", groupId());
getset( _simuWinGeo, "simuWindowGeometry" ,"" ,"Profiles", QString("profile%1").arg(index));
getset( _beeper, "Beeper" ,"" ,"Profiles", groupId());
getset( _countryCode, "countryCode" ,"" ,"Profiles", groupId());
getset( _display, "Display" ,"" ,"Profiles", groupId());
getset( _haptic, "Haptic" ,"" ,"Profiles", groupId());
getset( _speaker, "Speaker" ,"" ,"Profiles", groupId());
getset( _stickPotCalib, "StickPotCalib" ,"" ,"Profiles", groupId());
getset( _timeStamp, "TimeStamp" ,"" ,"Profiles", groupId());
getset( _trainerCalib, "TrainerCalib" ,"" ,"Profiles", groupId());
getset( _controlTypes, "ControlTypes" ,"" ,"Profiles", groupId());
getset( _controlNames, "ControlNames" ,"" ,"Profiles", groupId());
getset( _gsStickMode, "GSStickMode" ,0 ,"Profiles", groupId());
getset( _ppmMultiplier, "PPM_Multiplier" ,0 ,"Profiles", groupId());
getset( _vBatWarn, "vBatWarn" ,0 ,"Profiles", groupId());
getset( _vBatMin, "VbatMin" ,0 ,"Profiles", groupId());
getset( _vBatMax, "VbatMax" ,0 ,"Profiles", groupId());
getset( _txCurrentCalibration, "currentCalib" ,0 ,"Profiles", groupId());
getset( _txVoltageCalibration, "VbatCalib" ,0 ,"Profiles", groupId());
getset( _beeper, "Beeper" ,"" ,"Profiles", QString("profile%1").arg(index));
getset( _countryCode, "countryCode" ,"" ,"Profiles", QString("profile%1").arg(index));
getset( _display, "Display" ,"" ,"Profiles", QString("profile%1").arg(index));
getset( _haptic, "Haptic" ,"" ,"Profiles", QString("profile%1").arg(index));
getset( _speaker, "Speaker" ,"" ,"Profiles", QString("profile%1").arg(index));
getset( _stickPotCalib, "StickPotCalib" ,"" ,"Profiles", QString("profile%1").arg(index));
getset( _timeStamp, "TimeStamp" ,"" ,"Profiles", QString("profile%1").arg(index));
getset( _trainerCalib, "TrainerCalib" ,"" ,"Profiles", QString("profile%1").arg(index));
getset( _controlTypes, "ControlTypes" ,"" ,"Profiles", QString("profile%1").arg(index));
getset( _controlNames, "ControlNames" ,"" ,"Profiles", QString("profile%1").arg(index));
getset( _txCurrentCalibration, "currentCalib" ,0 ,"Profiles", QString("profile%1").arg(index));
getset( _gsStickMode, "GSStickMode" ,0 ,"Profiles", QString("profile%1").arg(index));
getset( _ppmMultiplier, "PPM_Multiplier" ,0 ,"Profiles", QString("profile%1").arg(index));
getset( _txVoltageCalibration, "VbatCalib" ,0 ,"Profiles", QString("profile%1").arg(index));
getset( _vBatWarn, "vBatWarn" ,0 ,"Profiles", QString("profile%1").arg(index));
getset( _vBatMin, "VbatMin" ,0 ,"Profiles", QString("profile%1").arg(index));
getset( _vBatMax, "VbatMax" ,0 ,"Profiles", QString("profile%1").arg(index));
getset( _simulatorOptions, "simulatorOptions", SimulatorOptions(), "Profiles", groupId());
}
QString Profile::groupId()
{
return QString("profile%1").arg(index);
}
@@ -536,9 +444,6 @@ QString AppData::mcu() { return _mcu; }
QString AppData::programmer() { return _programmer; }
QString AppData::sambaLocation() { return _sambaLocation; }
QString AppData::sambaPort() { return _sambaPort; }
QString AppData::lastSimulator() { return _lastSimulator; }
QString AppData::simuLastEepe() { return _simuLastEepe; }
QString AppData::simuLastFolder() { return _simuLastFolder; }
QString AppData::backupDir() { return _backupDir; }
QString AppData::gePath() { return _gePath; }
@@ -588,9 +493,6 @@ void AppData::mcu (const QString x) { store(x, _mcu,
void AppData::programmer (const QString x) { store(x, _programmer, "programmer" );}
void AppData::sambaLocation (const QString x) { store(x, _sambaLocation, "samba_location" );}
void AppData::sambaPort (const QString x) { store(x, _sambaPort, "samba_port" );}
void AppData::lastSimulator (const QString x) { store(x, _lastSimulator, "last_simulator" );}
void AppData::simuLastEepe (const QString x) { store(x, _simuLastEepe, "simuLastEepe" );}
void AppData::simuLastFolder (const QString x) { store(x, _simuLastFolder, "simuLastFolder" );}
void AppData::backupDir (const QString x) { store(x, _backupDir, "backupPath" );}
void AppData::gePath (const QString x) { store(x, _gePath, "gePath" );}
@@ -630,6 +532,8 @@ AppData::AppData()
void AppData::init()
{
qRegisterMetaTypeStreamOperators<SimulatorOptions>("SimulatorOptions");
//Initialize the profiles
for (int i=0; i<MAX_PROFILES; i++)
profile[i].init( i );
@@ -778,9 +682,6 @@ void AppData::init()
getset( _programmer, "programmer" ,"usbasp" );
getset( _sambaLocation, "samba_location" ,"" );
getset( _sambaPort, "samba_port" ,"\\USBserial\\COM23" );
getset( _lastSimulator, "last_simulator" ,"" );
getset( _simuLastEepe, "simuLastEepe" ,"" );
getset( _simuLastFolder, "simuLastFolder" ,"" );
getset( _backupDir, "backupPath" ,"" );
getset( _gePath, "gePath" ,"" );
@@ -792,13 +693,13 @@ void AppData::init()
getset( _snapshotDir, "snapshotpath" ,"" );
getset( _updatesDir, "lastUpdatesDir" ,"" );
getset( _outputDisplayDetails, "outputDisplayDetails" ,false );
getset( _enableBackup, "enableBackup" ,false );
getset( _backupOnFlash, "backupOnFlash" ,true );
getset( _checkHardwareCompatibility, "checkHardwareCompatibility" ,true );
getset( _useCompanionNightlyBuilds, "useCompanionNightlyBuilds" ,false );
getset( _useFirmwareNightlyBuilds, "useFirmwareNightlyBuilds" ,false );
getset( _outputDisplayDetails, "outputDisplayDetails" ,false );
getset( _checkHardwareCompatibility, "checkHardwareCompatibility" ,true );
getset( _useCompanionNightlyBuilds, "useCompanionNightlyBuilds" ,false );
getset( _useFirmwareNightlyBuilds, "useFirmwareNightlyBuilds" ,false );
getset( _jsSupport, "js_support" ,false );
getset( _maximized, "maximized" ,false );
@@ -820,6 +721,7 @@ void AppData::init()
getset( _theme, "theme" ,1 );
getset( _warningId, "warningId" ,0 );
getset( _simuLastProfId, "simuLastProfId" ,-1 );
}
QMap<int, QString> AppData::getActiveProfiles()
+38 -43
View File
@@ -30,8 +30,13 @@
#include <QString>
#include <QSettings>
#define COMPANY "OpenTX"
#define PRODUCT "Companion 2.2"
#include "simulator.h"
#define COMPANY "OpenTX"
#define COMPANY_DOMAIN "open-tx.org"
#define PRODUCT "Companion 2.2"
#define APP_COMPANION "OpenTX Companion"
#define APP_SIMULATOR "OpenTX Simulator"
#define MAX_PROFILES 15
#define MAX_JOYSTICKS 8
@@ -39,26 +44,24 @@
class CompStoreObj
{
public:
void clear (const QString tag1="", const QString tag2="", const QString tag3="");
void store(const QByteArray newArray, QByteArray &array, const QString tag, const QString group1="", const QString group2="" );
void store(const QStringList newSList, QStringList &stringList, const QString tag, const QString group1="", const QString group2="" );
void store(const QString newString, QString &string, const QString tag, const QString group1="", const QString group2="" );
void store(const bool newTruth, bool &truth, const QString tag, const QString group1="", const QString group2="" );
void store(const int newNumber, int &number, const QString tag, const QString group1="", const QString group2="" );
void clear (const QString & tag1 = "", const QString &tag2 = "", const QString &tag3 = "");
// Retrieval functions
void retrieve( QByteArray &array, const QString tag, const QString def, const QString group1="", const QString group2="" );
void retrieve( QStringList &stringList, const QString tag, const QString def, const QString group1="", const QString group2="" );
void retrieve( QString &string, const QString tag, const QString def, const QString group1="", const QString group2="" );
void retrieve( bool &truth, const QString tag, const bool def, const QString group1="", const QString group2="" );
void retrieve( int &number, const QString tag, const int def, const QString group1="", const QString group2="" );
// NOTE: OBJ_T can only be a standard or registered Qt type. To use custom types,
// either Q_DECLARE_METATYPE() them or add additional logic in retrieve() to handle QMetaType::User.
template <typename OBJ_T, typename TAG_T, typename GP1_T = QString, typename GP2_T = QString>
void store(const OBJ_T newValue, OBJ_T & destValue, const TAG_T tag, const GP1_T group1 = "", const GP2_T group2 = "");
template <typename OBJ_T, typename TAG_T, typename DEF_T, typename GP1_T = QString, typename GP2_T = QString>
void retrieve(OBJ_T & destValue, const TAG_T tag, const DEF_T def, const GP1_T group1 = "", const GP2_T group2 = "");
template <typename OBJ_T, typename TAG_T, typename DEF_T, typename GP1_T = QString, typename GP2_T = QString>
void getset(OBJ_T & value, const TAG_T tag, const DEF_T def, const GP1_T group1 = "", const GP2_T group2 = "" );
// this specialization is needed because QVariant::fromValue(const char *) fails
template <typename OBJ_T, typename TAG_T, typename GP1_T = QString, typename GP2_T = QString>
void getset(OBJ_T & value, const TAG_T tag, const char * def, const GP1_T group1 = "", const GP2_T group2 = "" );
// Retrieve and Store functions
void getset( QByteArray &array, const QString tag, const QString def, const QString group1="", const QString group2="" );
void getset( QStringList &stringList, const QString tag, const QString def, const QString group1="", const QString group2="" );
void getset( QString &string, const QString tag, const QString def, const QString group1="", const QString group2="" );
void getset( bool &truth, const QString tag, const bool def, const QString group1="", const QString group2="" );
void getset( int &number, const QString tag, const int def, const QString group1="", const QString group2="" );
};
class FwRevision: protected CompStoreObj
@@ -122,9 +125,6 @@ class Profile: protected CompStoreObj
int _channelOrder;
int _defaultMode;
// Simulator variables
QByteArray _simuWinGeo;
// Firmware Variables
QString _beeper;
QString _countryCode;
@@ -136,13 +136,16 @@ class Profile: protected CompStoreObj
QString _trainerCalib;
QString _controlTypes;
QString _controlNames;
int _txCurrentCalibration;
int _gsStickMode;
int _ppmMultiplier;
int _txVoltageCalibration;
int _vBatWarn;
int _vBatMin;
int _vBatMax;
int _txCurrentCalibration;
int _txVoltageCalibration;
// Simulator variables
SimulatorOptions _simulatorOptions;
public:
// All the get definitions
@@ -159,8 +162,6 @@ class Profile: protected CompStoreObj
int channelOrder() const;
int defaultMode() const;
QByteArray simuWinGeo() const;
QString beeper() const;
QString countryCode() const;
QString display() const;
@@ -179,6 +180,8 @@ class Profile: protected CompStoreObj
int vBatMin() const;
int vBatMax() const;
SimulatorOptions simulatorOptions() const;
// All the set definitions
void name (const QString);
void fwName (const QString);
@@ -193,8 +196,6 @@ class Profile: protected CompStoreObj
void channelOrder (const int);
void defaultMode (const int);
void simuWinGeo (const QByteArray);
void beeper (const QString);
void countryCode (const QString);
void display (const QString);
@@ -213,6 +214,8 @@ class Profile: protected CompStoreObj
void vBatMin (const int);
void vBatMax (const int);
void simulatorOptions (const SimulatorOptions &);
Profile();
Profile& operator=(const Profile&);
void remove();
@@ -220,6 +223,7 @@ class Profile: protected CompStoreObj
void init(int newIndex);
void initFwVariables();
void flush();
QString groupId();
};
#define BOOL_PROPERTY(name, dflt) \
@@ -231,12 +235,12 @@ class Profile: protected CompStoreObj
class AppData: protected CompStoreObj
{
BOOL_PROPERTY(enableBackup, false)
BOOL_PROPERTY(outputDisplayDetails, false)
BOOL_PROPERTY(backupOnFlash, true)
BOOL_PROPERTY(enableBackup, false)
BOOL_PROPERTY(backupOnFlash, true)
BOOL_PROPERTY(outputDisplayDetails, false)
BOOL_PROPERTY(checkHardwareCompatibility, true)
BOOL_PROPERTY(useCompanionNightlyBuilds, false)
BOOL_PROPERTY(useFirmwareNightlyBuilds, false)
BOOL_PROPERTY(useCompanionNightlyBuilds, false)
BOOL_PROPERTY(useFirmwareNightlyBuilds, false)
// All the global variables
public:
@@ -261,9 +265,6 @@ class AppData: protected CompStoreObj
QString _programmer;
QString _sambaLocation;
QString _sambaPort;
QString _lastSimulator;
QString _simuLastEepe;
QString _simuLastFolder;
QString _backupDir;
QString _gePath;
@@ -315,9 +316,6 @@ class AppData: protected CompStoreObj
QString programmer();
QString sambaLocation();
QString sambaPort();
QString lastSimulator();
QString simuLastEepe();
QString simuLastFolder();
QString backupDir();
QString gePath();
@@ -368,9 +366,6 @@ class AppData: protected CompStoreObj
void programmer (const QString);
void sambaLocation (const QString);
void sambaPort (const QString);
void lastSimulator (const QString);
void simuLastEepe (const QString);
void simuLastFolder (const QString);
void backupDir (const QString);
void gePath (const QString);