mirror of
https://github.com/EdgeTX/edgetx.git
synced 2025-07-26 17:55:12 +03:00
[Simulator] Add ability to update .otx files. (#4422)
* [SimulatorInterface] Add ability to read eeprom data back from simulator instance. * [Simulator] If started with .otx file, Simulator will now update (or create) the file upon exit using current radio/model data; Consolidate internal startup and shutdown code for handling various data sources.
This commit is contained in:
parent
2589e35ad5
commit
3a026a80f2
8 changed files with 103 additions and 63 deletions
|
@ -824,22 +824,23 @@ void startSimulation(QWidget * parent, RadioData & radioData, int modelIdx)
|
|||
}
|
||||
|
||||
SimulatorMainWindow * dialog = new SimulatorMainWindow(parent, simulator, flags);
|
||||
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()));
|
||||
if (!dialog->setRadioData(simuData)) {
|
||||
QMessageBox::critical(NULL, QObject::tr("Data Load Error"), QObject::tr("Error occurred while starting simulator."));
|
||||
delete dialog;
|
||||
delete simuData;
|
||||
return;
|
||||
}
|
||||
dialog->setRadioData(simuData);
|
||||
|
||||
dialog->setWindowModality(Qt::ApplicationModal);
|
||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||
dialog->start();
|
||||
dialog->show();
|
||||
|
||||
QObject::connect(dialog, &SimulatorMainWindow::destroyed, [simuData] (void) {
|
||||
// TODO simuData and Horus tmp directory is deleted on simulator close OR we could use it to get back data from the simulation
|
||||
delete simuData;
|
||||
});
|
||||
|
||||
dialog->show();
|
||||
}
|
||||
else {
|
||||
QMessageBox::warning(NULL,
|
||||
|
|
|
@ -172,10 +172,10 @@ void SimulatorDialog::setRadioSettings(const GeneralSettings settings)
|
|||
/*
|
||||
* 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 no/blank <dataSource>, 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
|
||||
* If <dataSource> 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.
|
||||
*/
|
||||
|
@ -185,7 +185,7 @@ bool SimulatorDialog::setStartupData(const QByteArray & dataSource, bool fromFil
|
|||
quint16 ret = 1;
|
||||
QString error;
|
||||
|
||||
// If <eeprom> is blank but we have a data path, use that for individual radio/model files.
|
||||
// If <dataSource> 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
|
||||
|
@ -197,33 +197,25 @@ bool SimulatorDialog::setStartupData(const QByteArray & dataSource, bool fromFil
|
|||
}
|
||||
}
|
||||
// Supposedly we're being given a file name to use, try that out.
|
||||
else if (fromFile) {
|
||||
else if (fromFile && !dataSource.isEmpty()) {
|
||||
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.
|
||||
return setRadioData(&simuData);
|
||||
ret = store.load(simuData);
|
||||
if (!ret && QFile(QString(dataSource)).exists()) {
|
||||
error = store.error();
|
||||
}
|
||||
else {
|
||||
startupData = dataSource; // save the file name for start()
|
||||
}
|
||||
}
|
||||
// again there's no way to tell what the error from Storage actually was, so if the file doesn't exist we'll create a new one
|
||||
else if (!dataSource.isEmpty() && !QFile(QString(dataSource)).exists()) {
|
||||
startupData = dataSource;
|
||||
startupFromFile = true;
|
||||
if (QString(dataSource).endsWith(".otx", Qt::CaseInsensitive)) {
|
||||
// no radios can work with .otx files directly, so we load contents into either
|
||||
// a temporary folder (Horus) or local data array (other radios) which we'll save back to .otx upon exit
|
||||
if ((ret = setRadioData(&simuData))) {
|
||||
startupFromFile = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ret = 0;
|
||||
error = store.error();
|
||||
// the binary file will be read/written directly by the fw interface, save the file name for simulator->start()
|
||||
startupData = dataSource;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Assume a byte array of radio data was passed, load it.
|
||||
|
@ -252,16 +244,27 @@ bool SimulatorDialog::setStartupData(const QByteArray & dataSource, bool fromFil
|
|||
|
||||
bool SimulatorDialog::setRadioData(RadioData * radioData)
|
||||
{
|
||||
bool ret = false;
|
||||
bool ret = true;
|
||||
|
||||
saveTempRadioData = (flags & SIMULATOR_FLAGS_STANDALONE);
|
||||
|
||||
if (IS_HORUS(m_board))
|
||||
ret = useTempDataPath(true);
|
||||
|
||||
if (ret) {
|
||||
if (radioDataPath.isEmpty()) {
|
||||
QByteArray eeprom(getEEpromSize(m_board), 0);
|
||||
if (firmware->getEEpromInterface()->save((uint8_t *)eeprom.data(), *radioData, 0, firmware->getCapability(SimulatorVariant)) > 0)
|
||||
ret = setStartupData(eeprom, false);
|
||||
startupData.fill(0, getEEpromSize(m_board));
|
||||
if (firmware->getEEpromInterface()->save((uint8_t *)startupData.data(), *radioData, 0, firmware->getCapability(SimulatorVariant)) <= 0)
|
||||
ret = false;
|
||||
}
|
||||
else {
|
||||
if ((ret = saveRadioData(radioData, radioDataPath)))
|
||||
radioSettings = radioData->generalSettings;
|
||||
ret = saveRadioData(radioData, radioDataPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret)
|
||||
radioSettings = radioData->generalSettings;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -306,7 +309,7 @@ bool SimulatorDialog::saveRadioData(RadioData * radioData, const QString & path,
|
|||
return false;
|
||||
}
|
||||
|
||||
bool SimulatorDialog::useTempDataPath(bool deleteOnClose, bool saveOnClose)
|
||||
bool SimulatorDialog::useTempDataPath(bool deleteOnClose)
|
||||
{
|
||||
if (deleteTempRadioData)
|
||||
deleteTempData();
|
||||
|
@ -316,9 +319,7 @@ bool SimulatorDialog::useTempDataPath(bool deleteOnClose, bool saveOnClose)
|
|||
setDataPath(tmpDir.path());
|
||||
tmpDir.setAutoRemove(false);
|
||||
deleteTempRadioData = deleteOnClose;
|
||||
saveTempRadioData = saveOnClose;
|
||||
qDebug() << __FILE__ << __LINE__ << "Created temporary settings directory" << radioDataPath
|
||||
<< "with delteOnClose:" << deleteOnClose << "with saveOnClose:" << saveOnClose;
|
||||
qDebug() << __FILE__ << __LINE__ << "Created temporary settings directory" << radioDataPath << "with delteOnClose:" << deleteOnClose;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
@ -334,24 +335,47 @@ bool SimulatorDialog::saveTempData()
|
|||
QString error;
|
||||
QString file = g.profile[radioProfileId].simulatorOptions().dataFile;
|
||||
|
||||
if (radioDataPath.isEmpty() || file.isEmpty())
|
||||
return ret;
|
||||
|
||||
if (!file.isEmpty()) {
|
||||
RadioData radioData;
|
||||
SdcardFormat sdcard(radioDataPath);
|
||||
if (!(ret = sdcard.load(radioData))) {
|
||||
error = sdcard.error();
|
||||
|
||||
if (radioDataPath.isEmpty()) {
|
||||
if (!startupData.isEmpty()) {
|
||||
if (!QFile(file).exists()) {
|
||||
QFile fh(file);
|
||||
if (!fh.open(QIODevice::WriteOnly))
|
||||
error = tr("Error saving data: could open file for writing: '%1'").arg(file);
|
||||
else
|
||||
fh.close();
|
||||
}
|
||||
|
||||
if (!firmware->getEEpromInterface()->load(radioData, (uint8_t *)startupData.constData(), getEEpromSize(m_board))) {
|
||||
error = tr("Error saving data: could not get data from simulator interface.");
|
||||
}
|
||||
else {
|
||||
radioData.fixModelFilenames();
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
SdcardFormat sdcard(radioDataPath);
|
||||
if (!(ret = sdcard.load(radioData)))
|
||||
error = sdcard.error();
|
||||
}
|
||||
if (ret) {
|
||||
Storage store(file);
|
||||
if (!(ret = store.write(radioData)))
|
||||
error = store.error();
|
||||
else
|
||||
qDebug() << __FILE__ << __LINE__ << "Saved radio data to file" << file;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
if (!ret) {
|
||||
if (error.isEmpty())
|
||||
error = tr("An unexpected error occurred while attempting to save radio data to file '%1'.").arg(file);
|
||||
QMessageBox::critical(this, tr("Data Save Error"), error);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -411,6 +435,10 @@ void SimulatorDialog::stop()
|
|||
{
|
||||
timer->stop();
|
||||
simulator->stop();
|
||||
if (saveTempRadioData) {
|
||||
startupData.fill(0, getEEpromSize(m_board));
|
||||
simulator->readEepromData(startupData);
|
||||
}
|
||||
}
|
||||
|
||||
void SimulatorDialog::restart()
|
||||
|
|
|
@ -73,7 +73,7 @@ class SimulatorDialog : public QWidget
|
|||
bool setRadioData(RadioData * radioData);
|
||||
bool 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 useTempDataPath(bool deleteOnClose = true);
|
||||
bool saveTempData();
|
||||
void deleteTempData();
|
||||
void saveState();
|
||||
|
|
|
@ -62,7 +62,7 @@ class SimulatorInterface
|
|||
|
||||
virtual ~SimulatorInterface() {}
|
||||
|
||||
virtual void setSdPath(const QString & sdPath = "", const QString & settingsPath = "") { };
|
||||
virtual void setSdPath(const QString & sdPath = "", const QString & settingsPath = "") { }
|
||||
|
||||
virtual void setVolumeGain(int value) { }
|
||||
|
||||
|
@ -72,6 +72,8 @@ class SimulatorInterface
|
|||
|
||||
virtual void stop() = 0;
|
||||
|
||||
virtual void readEepromData(QByteArray & dest) = 0;
|
||||
|
||||
virtual bool timer10ms() = 0;
|
||||
|
||||
virtual uint8_t * getLcd() = 0;
|
||||
|
|
|
@ -217,9 +217,9 @@ bool SimulatorMainWindow::setRadioData(RadioData * radioData)
|
|||
return m_simulatorWidget->setRadioData(radioData);
|
||||
}
|
||||
|
||||
bool SimulatorMainWindow::useTempDataPath(bool deleteOnClose, bool saveOnClose)
|
||||
bool SimulatorMainWindow::useTempDataPath(bool deleteOnClose)
|
||||
{
|
||||
return m_simulatorWidget->useTempDataPath(deleteOnClose, saveOnClose);
|
||||
return m_simulatorWidget->useTempDataPath(deleteOnClose);
|
||||
}
|
||||
|
||||
bool SimulatorMainWindow::setOptions(SimulatorOptions & options, bool withSave)
|
||||
|
|
|
@ -52,7 +52,7 @@ class SimulatorMainWindow : public QMainWindow
|
|||
~SimulatorMainWindow();
|
||||
|
||||
bool setRadioData(RadioData * radioData);
|
||||
bool useTempDataPath(bool deleteOnClose = true, bool saveOnClose = false);
|
||||
bool useTempDataPath(bool deleteOnClose = true);
|
||||
bool setOptions(SimulatorOptions & options, bool withSave = true);
|
||||
QMenu * createPopupMenu();
|
||||
|
||||
|
|
|
@ -116,6 +116,13 @@ void OpenTxSimulator::stop()
|
|||
StopEepromThread();
|
||||
}
|
||||
|
||||
void OpenTxSimulator::readEepromData(QByteArray & dest)
|
||||
{
|
||||
#if defined(EEPROM_SIZE)
|
||||
memcpy(dest.data(), eeprom, std::min<int>(EEPROM_SIZE, dest.size()));
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenTxSimulator::getValues(TxOutputs &outputs)
|
||||
{
|
||||
#define GETVALUES_IMPORT
|
||||
|
|
|
@ -46,6 +46,8 @@ class DLLEXPORT OpenTxSimulator : public SimulatorInterface {
|
|||
|
||||
virtual void stop();
|
||||
|
||||
virtual void readEepromData(QByteArray & dest);
|
||||
|
||||
virtual bool timer10ms();
|
||||
|
||||
virtual uint8_t * getLcd();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue