From e204031ef65a9f3a7cff6389908287f3cd687f78 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Tue, 27 Dec 2016 16:04:24 -0500 Subject: [PATCH] Standalone simulator startup options (#4164) * [simulator] Save and restore Simulator window sizes and positions (based on current radio profile). * [simulator] Set startup throttle stick lock in standalone sim based on default mode in current Companion radio profile; Add new flag indicating standalone simulator; Unique titles for simulator console windows. * [simulator] New/updated Simulator startup options: Radio profile used for standalone Simulator can now be separate from Companion; Allow selecting a radio profile and EEPROM image file (in addition to radio type) at startup via GUI and CLI; All startup settings are saved between uses. --- companion/src/constants.h | 12 + companion/src/flashfirmwaredialog.h | 12 - companion/src/helpers.cpp | 5 +- companion/src/simulation/simulatordialog.cpp | 132 +++---- companion/src/simulation/simulatordialog.h | 13 +- companion/src/simulator.cpp | 372 ++++++++++++++----- companion/src/storage/appdata.cpp | 28 +- companion/src/storage/appdata.h | 15 + 8 files changed, 406 insertions(+), 183 deletions(-) diff --git a/companion/src/constants.h b/companion/src/constants.h index 2fd97f7a7..18741f6f8 100644 --- a/companion/src/constants.h +++ b/companion/src/constants.h @@ -59,6 +59,18 @@ enum BoardEnum { #define CPN_MAX_KEYS 32 #define CPN_MAX_MOUSE_ANALOGS 2 +#define HEX_FILES_FILTER "HEX files (*.hex);;" +#define BIN_FILES_FILTER "BIN files (*.bin);;" +#define DFU_FILES_FILTER "DFU files (*.dfu);;" +#define EEPE_FILES_FILTER "EEPE EEPROM files (*.eepe);;" +#define OTX_FILES_FILTER "OpenTX files (*.otx);;" +#define EEPROM_FILES_FILTER "EEPE files (*.eepe *.bin *.hex);;" EEPE_FILES_FILTER BIN_FILES_FILTER HEX_FILES_FILTER +#define FLASH_FILES_FILTER "FLASH files (*.bin *.hex *.dfu);;" BIN_FILES_FILTER HEX_FILES_FILTER DFU_FILES_FILTER +#define EXTERNAL_EEPROM_FILES_FILTER "EEPROM files (*.bin *.hex);;" BIN_FILES_FILTER HEX_FILES_FILTER +#define ER9X_EEPROM_FILE_TYPE "ER9X_EEPROM_FILE" +#define EEPE_EEPROM_FILE_HEADER "EEPE EEPROM FILE" +#define EEPE_MODEL_FILE_HEADER "EEPE MODEL FILE" + const char * const ARROW_LEFT = "\xE2\x86\x90"; const char * const ARROW_UP = "\xE2\x86\x91"; const char * const ARROW_RIGHT = "\xE2\x86\x92"; diff --git a/companion/src/flashfirmwaredialog.h b/companion/src/flashfirmwaredialog.h index 259cb8d3d..554735c5b 100644 --- a/companion/src/flashfirmwaredialog.h +++ b/companion/src/flashfirmwaredialog.h @@ -26,18 +26,6 @@ #include "firmwareinterface.h" #include "xmlinterface.h" -#define HEX_FILES_FILTER "HEX files (*.hex);;" -#define BIN_FILES_FILTER "BIN files (*.bin);;" -#define DFU_FILES_FILTER "DFU files (*.dfu);;" -#define EEPE_FILES_FILTER "EEPE EEPROM files (*.eepe);;" -#define OTX_FILES_FILTER "OpenTX files (*.otx);;" -#define EEPROM_FILES_FILTER "EEPE files (*.eepe *.bin *.hex);;" EEPE_FILES_FILTER BIN_FILES_FILTER HEX_FILES_FILTER -#define FLASH_FILES_FILTER "FLASH files (*.bin *.hex *.dfu);;" BIN_FILES_FILTER HEX_FILES_FILTER DFU_FILES_FILTER -#define EXTERNAL_EEPROM_FILES_FILTER "EEPROM files (*.bin *.hex);;" BIN_FILES_FILTER HEX_FILES_FILTER -#define ER9X_EEPROM_FILE_TYPE "ER9X_EEPROM_FILE" -#define EEPE_EEPROM_FILE_HEADER "EEPE EEPROM FILE" -#define EEPE_MODEL_FILE_HEADER "EEPE MODEL FILE" - namespace Ui { class FlashFirmwareDialog; diff --git a/companion/src/helpers.cpp b/companion/src/helpers.cpp index 1ad8aee35..3fae62153 100644 --- a/companion/src/helpers.cpp +++ b/companion/src/helpers.cpp @@ -814,6 +814,7 @@ void startSimulation(QWidget * parent, RadioData & radioData, int modelIdx) 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); @@ -863,11 +864,11 @@ void startSimulation(QWidget * parent, RadioData & radioData, int modelIdx) } dialog->exec(); + delete dialog; + delete simuData; #if defined(WIN32) && defined(WIN_USE_CONSOLE_STDIO) FreeConsole(); #endif - delete dialog; - delete simuData; } else { QMessageBox::warning(NULL, diff --git a/companion/src/simulation/simulatordialog.cpp b/companion/src/simulation/simulatordialog.cpp index c38f82908..38366c4ff 100644 --- a/companion/src/simulation/simulatordialog.cpp +++ b/companion/src/simulation/simulatordialog.cpp @@ -37,47 +37,13 @@ void traceCb(const char * text) } } -void SimulatorDialog::traceCallback(const char * text) -{ - // this function is called from other threads - traceMutex.lock(); - // limit the size of list - if (traceList.size() < 1000) { - traceList.append(QString(text)); - } - traceMutex.unlock(); -} - -void SimulatorDialog::updateDebugOutput() -{ - traceMutex.lock(); - while (!traceList.isEmpty()) { - QString text = traceList.takeFirst(); - traceBuffer.append(text); - // limit the size of traceBuffer - if (traceBuffer.size() > 10*1024) { - traceBuffer.remove(0, 1*1024); - } - if (DebugOut) { - DebugOut->traceCallback(QString(text)); - } - } - traceMutex.unlock(); -} - -void SimulatorDialog::wheelEvent (QWheelEvent *event) -{ - if ( event->delta() != 0) { - simulator->wheelEvent(event->delta() > 0 ? 1 : -1); - } -} - SimulatorDialog::SimulatorDialog(QWidget * parent, SimulatorInterface *simulator, unsigned int flags): QDialog(parent), flags(flags), timer(NULL), lightOn(false), simulator(simulator), + radioProfileId(g.id()), lastPhase(-1), beepVal(0), TelemetrySimu(0), @@ -108,14 +74,16 @@ void SimulatorDialog::closeEvent (QCloseEvent *) { simulator->stop(); timer->stop(); - //g.simuWinGeo(GetCurrentFirmware()->getId(), saveGeometry()); + g.profile[radioProfileId].simuWinGeo(saveGeometry()); } void SimulatorDialog::showEvent(QShowEvent * event) { static bool firstShow = true; if (firstShow) { - if (flags & SIMULATOR_FLAGS_STICK_MODE_LEFT) { + restoreGeometry(g.profile[radioProfileId].simuWinGeo()); + + if (flags & SIMULATOR_FLAGS_STICK_MODE_LEFT || ((flags & SIMULATOR_FLAGS_STANDALONE) && (g.profile[radioProfileId].defaultMode() & 1))) { vJoyLeft->setStickConstraint(VirtualJoystickWidget::HOLD_Y, true); vJoyLeft->setStickY(1); } @@ -141,6 +109,29 @@ void SimulatorDialog::mouseReleaseEvent(QMouseEvent *event) } } +void SimulatorDialog::wheelEvent (QWheelEvent *event) +{ + if ( event->delta() != 0) { + simulator->wheelEvent(event->delta() > 0 ? 1 : -1); + } +} + +void SimulatorDialog::traceCallback(const char * text) +{ + // this function is called from other threads + traceMutex.lock(); + // limit the size of list + if (traceList.size() < 1000) { + traceList.append(QString(text)); + } + traceMutex.unlock(); +} + +void SimulatorDialog::setRadioProfileId(int value) +{ + radioProfileId = value; +} + void SimulatorDialog::onTrimPressed(int which) { trimPressed = which; @@ -257,13 +248,6 @@ void SimulatorDialog::keyReleaseEvent(QKeyEvent * event) } } -void SimulatorDialog::setupTimer() -{ - timer = new QTimer(this); - connect(timer, SIGNAL(timeout()), this, SLOT(onTimerEvent())); - timer->start(10); -} - template void SimulatorDialog::initUi(T * ui) { @@ -272,9 +256,6 @@ void SimulatorDialog::initUi(T * ui) windowName = tr("Simulating Radio (%1)").arg(GetCurrentFirmware()->getName()); setWindowTitle(windowName); - simulator->setSdPath(g.profile[g.id()].sdPath()); - simulator->setVolumeGain(g.profile[g.id()].volumeGain()); - lcd = ui->lcd; lcd->setData(simulator->getLcd(), lcdWidth, lcdHeight, lcdDepth); @@ -343,8 +324,6 @@ void SimulatorDialog::initUi(T * ui) setupGVarsDisplay(); setTrims(); - //restoreGeometry(g.simuWinGeo(GetCurrentFirmware()->getId())); - if (flags & SIMULATOR_FLAGS_NOTX) tabWidget->setCurrentIndex(1); @@ -500,6 +479,23 @@ void SimulatorDialog::onButtonPressed(int value) } } +void SimulatorDialog::updateDebugOutput() +{ + traceMutex.lock(); + while (!traceList.isEmpty()) { + QString text = traceList.takeFirst(); + traceBuffer.append(text); + // limit the size of traceBuffer + if (traceBuffer.size() > 10*1024) { + traceBuffer.remove(0, 1*1024); + } + if (DebugOut) { + DebugOut->traceCallback(QString(text)); + } + } + traceMutex.unlock(); +} + void SimulatorDialog::onTimerEvent() { static unsigned int lcd_counter = 0; @@ -555,20 +551,25 @@ void SimulatorDialog::onTimerEvent() updateDebugOutput(); } -void SimulatorDialog::centerSticks() -{ - if (vJoyLeft) - vJoyLeft->centerStick(); - - if (vJoyRight) - vJoyRight->centerStick(); -} - -void SimulatorDialog::start(QByteArray & eeprom) +void SimulatorDialog::startCommon() { lastPhase = -1; numGvars = GetCurrentFirmware()->getCapability(Gvars); numFlightModes = GetCurrentFirmware()->getCapability(FlightModes); + simulator->setSdPath(g.profile[radioProfileId].sdPath()); + simulator->setVolumeGain(g.profile[radioProfileId].volumeGain()); +} + +void SimulatorDialog::setupTimer() +{ + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(onTimerEvent())); + timer->start(10); +} + +void SimulatorDialog::start(QByteArray & eeprom) +{ + startCommon(); simulator->start(eeprom, (flags & SIMULATOR_FLAGS_NOTX) ? false : true); getValues(); setupTimer(); @@ -576,14 +577,21 @@ void SimulatorDialog::start(QByteArray & eeprom) void SimulatorDialog::start(const char * filename) { - lastPhase = -1; - numGvars = GetCurrentFirmware()->getCapability(Gvars); - numFlightModes = GetCurrentFirmware()->getCapability(FlightModes); + startCommon(); simulator->start(filename); getValues(); setupTimer(); } +void SimulatorDialog::centerSticks() +{ + if (vJoyLeft) + vJoyLeft->centerStick(); + + if (vJoyRight) + vJoyRight->centerStick(); +} + void SimulatorDialog::setTrims() { typedef VirtualJoystickWidget VJW; diff --git a/companion/src/simulation/simulatordialog.h b/companion/src/simulation/simulatordialog.h index 74cba69ed..12580c8d8 100644 --- a/companion/src/simulation/simulatordialog.h +++ b/companion/src/simulation/simulatordialog.h @@ -54,11 +54,12 @@ class VirtualJoystickWidget; #define SIMULATOR_FLAGS_S1 4 #define SIMULATOR_FLAGS_S2 8 #define SIMULATOR_FLAGS_S3 16 -#define SIMULATOR_FLAGS_S4 32 // reserved for the future +#define SIMULATOR_FLAGS_S4 32 // reserved for the future #define SIMULATOR_FLAGS_S1_MULTI 64 #define SIMULATOR_FLAGS_S2_MULTI 128 #define SIMULATOR_FLAGS_S3_MULTI 256 -#define SIMULATOR_FLAGS_S4_MULTI 512 // reserved for the future +#define SIMULATOR_FLAGS_S4_MULTI 512 // reserved for the future +#define SIMULATOR_FLAGS_STANDALONE 1024 // started from stanalone simulator void traceCb(const char * text); @@ -72,9 +73,9 @@ class SimulatorDialog : public QDialog void start(const char * filename); void start(QByteArray & eeprom); + void setRadioProfileId(int value); virtual void traceCallback(const char * text); - protected: template void initUi(T * ui); virtual void setLightOn(bool enable) { } @@ -109,16 +110,18 @@ class SimulatorDialog : public QDialog #endif SimulatorInterface *simulator; + int radioProfileId; unsigned int lastPhase; - void setupTimer(); QFrame * createLogicalSwitch(QWidget * parent, int switchNo, QVector & labels); void setupOutputsDisplay(); void setupGVarsDisplay(); + void startCommon(); + void setupTimer(); + void centerSticks(); void setTrims(); - void setValues(); virtual void getValues() = 0; int getValue(qint8 i); diff --git a/companion/src/simulator.cpp b/companion/src/simulator.cpp index c69938ffb..93963829e 100644 --- a/companion/src/simulator.cpp +++ b/companion/src/simulator.cpp @@ -19,20 +19,22 @@ */ #include -#include +//#include #include #include #include -#include -#include -#include #include #include +#include +#include +#include +#include #if defined(JOYSTICKS) || defined(SIMU_AUDIO) #include #undef main #endif #include "simulatordialog.h" +#include "constants.h" #include "eeprominterface.h" #include "appdata.h" #include "qxtcommandoptions.h" @@ -62,13 +64,158 @@ class MyProxyStyle : public QProxyStyle }; #endif -void showMessage(const QString & message, enum QMessageBox::Icon icon = QMessageBox::NoIcon) { +typedef struct +{ + int profileId; + QString firmwareId; + QString eepromFileName; +} simulatorOptions_t; + + +QDir g_eepromDirectory; + +int finish(int exitCode); + +void showMessage(const QString & message, enum QMessageBox::Icon icon = QMessageBox::NoIcon) +{ QMessageBox msgBox; msgBox.setText(message); msgBox.setIcon(icon); msgBox.exec(); } +QString radioEepromFileName(QString firmwareId) +{ + QString eepromFileName = ""; + 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); + } + } + eepromFileName = QString("eeprom-%1.bin").arg(radioId); + 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.
" \ + "Create or edit profiles using the Companion application.")); + QMapIterator 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.
" \ + "The radio type specified in the selected profile is used by default.")); + cbType->addItems(registered_simulators.keys()); + cbType->setCurrentIndex(cbType->findText(opts.firmwareId)); + form->addRow(label, cbType); + + label = QObject::tr("EEPROM Image:"); + QLineEdit * fwFile = new QLineEdit(opts.eepromFileName, dialog); + fwFile->setToolTip(QObject::tr("EEPROM image file to use. A new file with a default image will be created if necessary.
" \ + "NOTE: 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 EEPROM image file...")); + 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); + + 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())); + + // set new default radio type when profile choice changes + QObject::connect(cbProf, static_cast(&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(&QComboBox::currentIndexChanged), [cbType, fwFile](int index) { + if (index < 0) + return; + fwFile->setText(radioEepromFileName(cbType->currentText())); + }); + + // connect button to file selector dialog + QObject::connect(fwBtn, &QToolButton::clicked, [dialog, fwFile, cbType, opts](bool) { + QString filter = QObject::tr((cbType->currentText().contains("horus") ? OTX_FILES_FILTER : EEPROM_FILES_FILTER)); + filter += QObject::tr("All files (*.*)"); + QString file = QFileDialog::getSaveFileName(dialog, QObject::tr("Select EEPROM image"), opts.eepromFileName, + filter, NULL, QFileDialog::DontConfirmOverwrite); + if (!file.isEmpty()) + fwFile->setText(file); + }); + + // go + if (dialog->exec() == QDialog::Accepted) { + opts.profileId = cbProf->currentData().toInt(); + opts.firmwareId = cbType->currentText(); + opts.eepromFileName = fwFile->text(); + ret = true; + } + + dialog->deleteLater(); + return ret; +} + +void sharedHelpText(QTextStream &stream) +{ + // list all available profiles + stream << endl << QObject::tr("Available profiles:") << endl; + QMapIterator pi(g.getActiveProfiles()); + while (pi.hasNext()) { + pi.next(); + stream << "\t" << QObject::tr("ID: ") << pi.key() << QObject::tr(" Name: ") << pi.value() << endl; + } + // list all available radios + stream << endl << QObject::tr("Available radios:") << endl; + foreach(QString name, registered_simulators.keys()) { + stream << "\t" << name << endl; + } +} + int main(int argc, char *argv[]) { Q_INIT_RESOURCE(companion); @@ -77,6 +224,7 @@ int main(int argc, char *argv[]) #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); @@ -117,120 +265,144 @@ int main(int argc, char *argv[]) #endif SimulatorDialog *dialog; - QString eepromFileName; - QDir eedir; - QFile file; + simulatorOptions_t simOptions; + QxtCommandOptions cliOptions; + bool cliOptsFound = false; + + 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(); registerOpenTxFirmwares(); - eedir = QDir(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); - if (!eedir.exists("OpenTX")) { - if (!eedir.mkpath("OpenTX")) { - showMessage(QObject::tr("WARNING: couldn't create directory for EEPROM:\n%1").arg(eedir.absoluteFilePath("OpenTX")), QMessageBox::Warning); - } - } - eedir.cd("OpenTX"); - - QStringList firmwareIds; - int currentIdx = 0; - foreach(SimulatorFactory *factory, registered_simulators) { - firmwareIds << factory->name(); - if (factory->name() == g.lastSimulator()) { - currentIdx = firmwareIds.size() - 1; - } + if (!registered_simulators.size()) { + showMessage(QObject::tr("ERROR: No simulator libraries available."), QMessageBox::Critical); + return finish(3); } - QxtCommandOptions options; - options.add("radio", "radio to simulate", QxtCommandOptions::ValueRequired); - options.alias("radio", "r"); - options.add("help", "show this help text"); - options.alias("help", "h"); - options.parse(QCoreApplication::arguments()); - if (options.count("help") || options.showUnrecognizedWarning()) { + cliOptions.add("profile", QObject::tr("Radio profile ID or Name to use for simulator."), QxtCommandOptions::ValueRequired); + cliOptions.alias("profile", "p"); + cliOptions.add("radio", QObject::tr("Radio type to simulate (usually defined in profile)."), QxtCommandOptions::ValueRequired); + cliOptions.alias("radio", "r"); + cliOptions.add("help", QObject::tr("show this help text")); + cliOptions.alias("help", "h"); + cliOptions.parse(QCoreApplication::arguments()); + if (cliOptions.count("help") || cliOptions.showUnrecognizedWarning()) { QString msg; QTextStream stream(&msg); - stream << "Usage: simulator [OPTION]... [EEPROM.BIN FILE] " << endl << endl; - stream << "Options:" << endl; - options.showUsage(false, stream); - // list all available radios - stream << endl << "Available radios:" << endl; - foreach(QString name, firmwareIds) { - stream << "\t" << name << endl; - } + stream << QObject::tr("Usage: simulator [OPTION]... [EEPROM.BIN FILE] ") << endl << endl; + stream << QObject::tr("Options:") << endl; + cliOptions.showUsage(false, stream); + sharedHelpText(stream); // display showMessage(msg, QMessageBox::Information); - return 1; + return finish(1); } - - bool ok = false; - QString firmwareId; - if (options.count("radio") == 1) { - firmwareId = options.value("radio").toString(); - if (firmwareIds.contains(firmwareId)) { - ok = true; - } - } - if (!ok) { - firmwareId = QInputDialog::getItem(0, QObject::tr("Radio type"), - QObject::tr("Which radio type do you want to simulate?"), - firmwareIds, currentIdx, false, &ok); - } - qDebug() << "firmwareId" << firmwareId; - - if (ok && !firmwareId.isEmpty()) { - if (firmwareId != g.lastSimulator()) { - g.lastSimulator(firmwareId); - } - QString radioId; - int pos = firmwareId.indexOf("-"); - if (pos > 0) { - radioId = firmwareId.mid(pos+1); - pos = radioId.lastIndexOf("-"); - if (pos > 0) { - radioId = radioId.mid(0, pos); - } - } - qDebug() << "radioId" << radioId; - current_firmware_variant = GetFirmware(firmwareId); - qDebug() << "current_firmware_variant" << current_firmware_variant->getName(); - - if (options.positional().isEmpty()) { - eepromFileName = QString("eeprom-%1.bin").arg(radioId); - eepromFileName = eedir.filePath(eepromFileName.toLatin1()); - } - else { - eepromFileName = options.positional()[0]; - } - qDebug() << "eepromFileName" << eepromFileName; - // TODO display used eeprom filename somewhere - - SimulatorFactory * factory = getSimulatorFactory(firmwareId); - if (!factory) { - showMessage(QObject::tr("ERROR: Simulator %1 not found").arg(firmwareId), QMessageBox::Critical); - return 2; - } - if (factory->type() == BOARD_HORUS) - dialog = new SimulatorDialogHorus(NULL, factory->create()); - else if (factory->type() == BOARD_FLAMENCO) - dialog = new SimulatorDialogFlamenco(NULL, factory->create()); - else if (factory->type() == BOARD_TARANIS_X9D || factory->type() == BOARD_TARANIS_X9DP || factory->type() == BOARD_TARANIS_X9E) - dialog = new SimulatorDialogTaranis(NULL, factory->create(), SIMULATOR_FLAGS_S1|SIMULATOR_FLAGS_S2); - else - dialog = new SimulatorDialog9X(NULL, factory->create()); + if (cliOptions.count("radio") == 1) { + simOptions.firmwareId = cliOptions.value("radio").toString(); + cliOptsFound = true; } else { - return 0; + simOptions.firmwareId = g.lastSimulator(); } - dialog->show(); - dialog->start(eepromFileName.toLatin1().constData()); + 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(); + } + cliOptsFound = true; + } + else if (g.simuLastProfId() != -1) { + simOptions.profileId = g.simuLastProfId(); + } + else { + simOptions.profileId = g.id(); + } + if (!cliOptions.positional().isEmpty()) { + simOptions.eepromFileName = cliOptions.positional()[0]; + cliOptsFound = true; + } + else if (cliOptsFound || g.simuLastEepe().isEmpty()) { + simOptions.eepromFileName = radioEepromFileName(simOptions.firmwareId); + } + else { + simOptions.eepromFileName = g.simuLastEepe(); + } + + if (!cliOptsFound || simOptions.profileId == -1 || simOptions.firmwareId.isEmpty() || simOptions.eepromFileName.isEmpty()) { + if (!startupOptionsDialog(simOptions)) { + return finish(0); + } + } + qDebug() << "firmwareId" << simOptions.firmwareId << "profileId" << simOptions.profileId << "eepromFileName" << simOptions.eepromFileName; + + if (simOptions.profileId < 0 || simOptions.firmwareId.isEmpty() || simOptions.eepromFileName.isEmpty()) { + showMessage(QObject::tr("ERROR: Couldn't start simulator, missing radio/profile/EEPROM file.\nProfile ID: [%1]; Radio ID: [%2]\nEEPROM File: [%3]") + .arg(simOptions.profileId).arg(simOptions.firmwareId).arg(simOptions.eepromFileName), QMessageBox::Critical); + return finish(1); + } + if (!g.getActiveProfiles().contains(simOptions.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); + sharedHelpText(stream); + showMessage(msg, QMessageBox::Critical); + return finish(2); + } + + SimulatorFactory * factory = getSimulatorFactory(simOptions.firmwareId); + if (!factory) { + showMessage(QObject::tr("ERROR: Simulator %1 not found").arg(simOptions.firmwareId), QMessageBox::Critical); + return finish(2); + } + + g.simuLastProfId(simOptions.profileId); + g.lastSimulator(simOptions.firmwareId); + g.simuLastEepe(simOptions.eepromFileName); + + uint32_t flags = SIMULATOR_FLAGS_STANDALONE; + + if (factory->type() == BOARD_HORUS) + dialog = new SimulatorDialogHorus(NULL, factory->create(), flags); + else if (factory->type() == BOARD_FLAMENCO) + dialog = new SimulatorDialogFlamenco(NULL, factory->create(), flags); + else if (factory->type() == BOARD_TARANIS_X9D || factory->type() == BOARD_TARANIS_X9DP || factory->type() == BOARD_TARANIS_X9E) + dialog = new SimulatorDialogTaranis(NULL, factory->create(), flags | SIMULATOR_FLAGS_S1 | SIMULATOR_FLAGS_S2); + else + dialog = new SimulatorDialog9X(NULL, factory->create(), flags); + + dialog->setRadioProfileId(simOptions.profileId); + dialog->start(simOptions.eepromFileName.toLatin1().constData()); + + dialog->show(); int result = app.exec(); - delete dialog; + dialog->deleteLater(); + return finish(result); +} + +int finish(int exitCode) +{ unregisterSimulators(); unregisterOpenTxFirmwares(); @@ -241,5 +413,5 @@ int main(int argc, char *argv[]) FreeConsole(); #endif - return result; + return exitCode; } diff --git a/companion/src/storage/appdata.cpp b/companion/src/storage/appdata.cpp index 8dbeaf101..2ce399e5c 100644 --- a/companion/src/storage/appdata.cpp +++ b/companion/src/storage/appdata.cpp @@ -310,6 +310,8 @@ 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; } @@ -342,6 +344,8 @@ void Profile::penableBackup (const bool x) { store(x, _penableBackup, "penabl 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::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));} @@ -352,10 +356,10 @@ void Profile::timeStamp (const QString x) { store(x, _timeStamp, "TimeSt 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::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::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));} @@ -464,6 +468,8 @@ void Profile::init(int newIndex) _channelOrder = 0; _defaultMode = 1; + _simuWinGeo = QByteArray(); + initFwVariables(); // Do not write empty profiles to disk except the default (0) profile. @@ -489,6 +495,8 @@ void Profile::flush() getset( _channelOrder, "default_channel_order" ,0 ,"Profiles", QString("profile%1").arg(index)); getset( _defaultMode, "default_mode" ,1 ,"Profiles", QString("profile%1").arg(index)); + getset( _simuWinGeo, "simuWindowGeometry" ,"" ,"Profiles", QString("profile%1").arg(index)); + 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)); @@ -529,6 +537,7 @@ 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::backupDir() { return _backupDir; } QString AppData::gePath() { return _gePath; } @@ -559,6 +568,7 @@ int AppData::jsCtrl() { return _jsCtrl; } int AppData::id() { return _id; } int AppData::theme() { return _theme; } int AppData::warningId() { return _warningId; } +int AppData::simuLastProfId() { return _simuLastProfId; } // Set declarations void AppData::recentFiles (const QStringList x) { store(x, _recentFiles, "recentFileList" );} @@ -578,6 +588,7 @@ void AppData::programmer (const QString x) { store(x, _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::backupDir (const QString x) { store(x, _backupDir, "backupPath" );} void AppData::gePath (const QString x) { store(x, _gePath, "gePath" );} @@ -608,6 +619,7 @@ void AppData::jsCtrl (const int x) { store(x, _jsCtrl, void AppData::id (const int x) { store(x, _id, "profileId" );} void AppData::theme (const int x) { store(x, _theme, "theme" );} void AppData::warningId (const int x) { store(x, _warningId, "warningId" );} +void AppData::simuLastProfId (const int x) { store(x, _simuLastProfId, "simuLastProfId" );} // Constructor AppData::AppData() @@ -765,6 +777,7 @@ void AppData::init() getset( _sambaLocation, "samba_location" ,"" ); getset( _sambaPort, "samba_port" ,"\\USBserial\\COM23" ); getset( _lastSimulator, "last_simulator" ,"" ); + getset( _simuLastEepe, "simuLastEepe" ,"" ); getset( _backupDir, "backupPath" ,"" ); getset( _gePath, "gePath" ,"" ); @@ -803,4 +816,15 @@ void AppData::init() getset( _id, "profileId" ,0 ); getset( _theme, "theme" ,1 ); getset( _warningId, "warningId" ,0 ); + getset( _simuLastProfId, "simuLastProfId" ,-1 ); +} + +QMap AppData::getActiveProfiles() +{ + QMap active; + for (int i=0; i getActiveProfiles(); }; extern AppData g;