From cd5a4db87694174110e14539dc2790e322c55776 Mon Sep 17 00:00:00 2001 From: f4exb Date: Thu, 24 Sep 2020 05:38:05 +0200 Subject: [PATCH] Feature plugins framework: presets --- plugins/feature/simpleptt/CMakeLists.txt | 2 + plugins/feature/simpleptt/simplepttplugin.cpp | 6 + plugins/feature/simpleptt/simplepttplugin.h | 1 + .../simpleptt/simplepttwebapiadapter.cpp | 50 +++ .../simpleptt/simplepttwebapiadapter.h | 49 +++ sdrbase/CMakeLists.txt | 4 + sdrbase/channel/channelwebapiadapter.h | 2 +- sdrbase/feature/featureutils.cpp | 28 ++ sdrbase/feature/featureutils.h | 31 ++ sdrbase/feature/featurewebapiadapter.h | 69 +++ sdrbase/plugin/plugininterface.h | 8 + sdrbase/plugin/pluginmanager.cpp | 12 + sdrbase/plugin/pluginmanager.h | 2 +- sdrbase/resources/webapi/doc/html2/index.html | 4 + sdrbase/settings/featuresetpreset.cpp | 97 ++++ sdrbase/settings/featuresetpreset.h | 81 ++++ sdrbase/settings/mainsettings.cpp | 135 +++++- sdrbase/settings/mainsettings.h | 26 +- sdrbase/webapi/webapiadapterbase.cpp | 130 ++++++ sdrbase/webapi/webapiadapterbase.h | 24 +- sdrbase/webapi/webapiadapterinterface.cpp | 24 + sdrbase/webapi/webapiadapterinterface.h | 13 + sdrbase/webapi/webapirequestmapper.cpp | 182 +++++++- sdrbase/webapi/webapirequestmapper.h | 32 ++ sdrgui/CMakeLists.txt | 2 + sdrgui/feature/featureuiset.cpp | 76 ++++ sdrgui/feature/featureuiset.h | 5 + sdrgui/gui/featurepresetsdialog.cpp | 413 ++++++++++++++++++ sdrgui/gui/featurepresetsdialog.h | 82 ++++ sdrgui/gui/featurepresetsdialog.ui | 261 +++++++++++ sdrgui/gui/featuresdock.cpp | 19 + sdrgui/gui/featuresdock.h | 9 + sdrgui/mainwindow.cpp | 62 ++- sdrgui/mainwindow.h | 72 +++ sdrgui/resources/res.qrc | 1 + sdrgui/resources/star.png | Bin 0 -> 1043 bytes sdrgui/webapi/webapiadaptergui.cpp | 25 ++ sdrsrv/feature/featureset.cpp | 74 ++++ sdrsrv/feature/featureset.h | 3 + sdrsrv/maincore.cpp | 57 ++- sdrsrv/maincore.h | 70 +++ sdrsrv/webapi/webapiadaptersrv.cpp | 25 ++ 42 files changed, 2242 insertions(+), 26 deletions(-) create mode 100644 plugins/feature/simpleptt/simplepttwebapiadapter.cpp create mode 100644 plugins/feature/simpleptt/simplepttwebapiadapter.h create mode 100644 sdrbase/feature/featureutils.cpp create mode 100644 sdrbase/feature/featureutils.h create mode 100644 sdrbase/feature/featurewebapiadapter.h create mode 100644 sdrbase/settings/featuresetpreset.cpp create mode 100644 sdrbase/settings/featuresetpreset.h create mode 100644 sdrgui/gui/featurepresetsdialog.cpp create mode 100644 sdrgui/gui/featurepresetsdialog.h create mode 100644 sdrgui/gui/featurepresetsdialog.ui create mode 100644 sdrgui/resources/star.png diff --git a/plugins/feature/simpleptt/CMakeLists.txt b/plugins/feature/simpleptt/CMakeLists.txt index 8902dd59d..4d48f67f0 100644 --- a/plugins/feature/simpleptt/CMakeLists.txt +++ b/plugins/feature/simpleptt/CMakeLists.txt @@ -6,6 +6,7 @@ set(simpleptt_SOURCES simplepttplugin.cpp simplepttworker.cpp simplepttreport.cpp + simplepttwebapiadapter.cpp ) set(simpleptt_HEADERS @@ -14,6 +15,7 @@ set(simpleptt_HEADERS simplepttplugin.h simplepttworker.h simplepttreport.h + simplepttwebapiadapter.h ) include_directories( diff --git a/plugins/feature/simpleptt/simplepttplugin.cpp b/plugins/feature/simpleptt/simplepttplugin.cpp index e99444ae4..c9d8d8eff 100644 --- a/plugins/feature/simpleptt/simplepttplugin.cpp +++ b/plugins/feature/simpleptt/simplepttplugin.cpp @@ -24,6 +24,7 @@ #endif #include "simpleptt.h" #include "simplepttplugin.h" +#include "simplepttwebapiadapter.h" const PluginDescriptor SimplePTTPlugin::m_pluginDescriptor = { SimplePTT::m_featureId, @@ -72,3 +73,8 @@ Feature* SimplePTTPlugin::createFeature(WebAPIAdapterInterface* webAPIAdapterInt { return new SimplePTT(webAPIAdapterInterface); } + +FeatureWebAPIAdapter* SimplePTTPlugin::createFeatureWebAPIAdapter() const +{ + return new SimplePTTWebAPIAdapter(); +} diff --git a/plugins/feature/simpleptt/simplepttplugin.h b/plugins/feature/simpleptt/simplepttplugin.h index 0d32b88fc..330f65d2a 100644 --- a/plugins/feature/simpleptt/simplepttplugin.h +++ b/plugins/feature/simpleptt/simplepttplugin.h @@ -36,6 +36,7 @@ public: virtual PluginInstanceGUI* createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const; virtual Feature* createFeature(WebAPIAdapterInterface *webAPIAdapterInterface) const; + virtual FeatureWebAPIAdapter* createFeatureWebAPIAdapter() const; private: static const PluginDescriptor m_pluginDescriptor; diff --git a/plugins/feature/simpleptt/simplepttwebapiadapter.cpp b/plugins/feature/simpleptt/simplepttwebapiadapter.cpp new file mode 100644 index 000000000..646f22ec0 --- /dev/null +++ b/plugins/feature/simpleptt/simplepttwebapiadapter.cpp @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB. // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "SWGFeatureSettings.h" +#include "simpleptt.h" +#include "simplepttwebapiadapter.h" + +SimplePTTWebAPIAdapter::SimplePTTWebAPIAdapter() +{} + +SimplePTTWebAPIAdapter::~SimplePTTWebAPIAdapter() +{} + +int SimplePTTWebAPIAdapter::webapiSettingsGet( + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setSimplePttSettings(new SWGSDRangel::SWGSimplePTTSettings()); + response.getSimplePttSettings()->init(); + SimplePTT::webapiFormatFeatureSettings(response, m_settings); + + return 200; +} + +int SimplePTTWebAPIAdapter::webapiSettingsPutPatch( + bool force, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + SimplePTT::webapiUpdateFeatureSettings(m_settings, featureSettingsKeys, response); + + return 200; +} diff --git a/plugins/feature/simpleptt/simplepttwebapiadapter.h b/plugins/feature/simpleptt/simplepttwebapiadapter.h new file mode 100644 index 000000000..64dbe5b3c --- /dev/null +++ b/plugins/feature/simpleptt/simplepttwebapiadapter.h @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB. // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_SIMPLEPTT_WEBAPIADAPTER_H +#define INCLUDE_SIMPLEPTT_WEBAPIADAPTER_H + +#include "feature/featurewebapiadapter.h" +#include "simplepttsettings.h" + +/** + * Standalone API adapter only for the settings + */ +class SimplePTTWebAPIAdapter : public FeatureWebAPIAdapter { +public: + SimplePTTWebAPIAdapter(); + virtual ~SimplePTTWebAPIAdapter(); + + virtual QByteArray serialize() const { return m_settings.serialize(); } + virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); } + + virtual int webapiSettingsGet( + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage); + +private: + SimplePTTSettings m_settings; +}; + +#endif // INCLUDE_SIMPLEPTT_WEBAPIADAPTER_H diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index 34f16759e..be734bbf1 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -138,9 +138,11 @@ set(sdrbase_SOURCES device/deviceutils.cpp feature/feature.cpp + feature/featureutils.cpp limerfe/limerfeusbcalib.cpp + settings/featuresetpreset.cpp settings/preferences.cpp settings/preset.cpp settings/mainsettings.cpp @@ -292,6 +294,7 @@ set(sdrbase_HEADERS device/deviceutils.h feature/feature.h + feature/featureutils.h limerfe/limerfeusbcalib.h @@ -300,6 +303,7 @@ set(sdrbase_HEADERS plugin/pluginapi.h plugin/pluginmanager.h + settings/featuresetpreset.h settings/preferences.h settings/preset.h settings/mainsettings.h diff --git a/sdrbase/channel/channelwebapiadapter.h b/sdrbase/channel/channelwebapiadapter.h index 2fd6fa175..65db4b97e 100644 --- a/sdrbase/channel/channelwebapiadapter.h +++ b/sdrbase/channel/channelwebapiadapter.h @@ -66,4 +66,4 @@ public: } }; -#endif // SDRBASE_CHANNEL_CHANNELWEBAPIADAPER_H_ \ No newline at end of file +#endif // SDRBASE_CHANNEL_CHANNELWEBAPIADAPER_H_ diff --git a/sdrbase/feature/featureutils.cpp b/sdrbase/feature/featureutils.cpp new file mode 100644 index 000000000..2f10a3abb --- /dev/null +++ b/sdrbase/feature/featureutils.cpp @@ -0,0 +1,28 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "featureutils.h" + +bool FeatureUtils::compareFeatureURIs(const QString& registeredFeatureURI, const QString& xFeatureURI) +{ + return registeredFeatureURI == getRegisteredFeatureURI(xFeatureURI); +} + +QString FeatureUtils::getRegisteredFeatureURI(const QString& xFeatureURI) +{ + return xFeatureURI; +} diff --git a/sdrbase/feature/featureutils.h b/sdrbase/feature/featureutils.h new file mode 100644 index 000000000..90790992d --- /dev/null +++ b/sdrbase/feature/featureutils.h @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRBASE_FEATURE_FEATUREUTILS_H_ +#define SDRBASE_FEATURE_FEATUREUTILS_H_ + +#include +#include "export.h" + +class SDRBASE_API FeatureUtils +{ +public: + static bool compareFeatureURIs(const QString& registeredFeatureURI, const QString& xFeatureURI); + static QString getRegisteredFeatureURI(const QString& xFeatureURI); +}; + +#endif // SDRBASE_FEATURE_FEATUREUTILS_H_ diff --git a/sdrbase/feature/featurewebapiadapter.h b/sdrbase/feature/featurewebapiadapter.h new file mode 100644 index 000000000..dfda4c67d --- /dev/null +++ b/sdrbase/feature/featurewebapiadapter.h @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// Interface for static web API adapters used for preset serialization and // +// deserialization // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRBASE_FEATURE_FEATUREWEBAPIADAPER_H_ +#define SDRBASE_FEATURE_FEATUREWEBAPIADAPER_H_ + +#include +#include + +#include "export.h" + +namespace SWGSDRangel +{ + class SWGFeatureSettings; +} + +class SDRBASE_API FeatureWebAPIAdapter +{ +public: + FeatureWebAPIAdapter() {} + virtual ~FeatureWebAPIAdapter() {} + virtual QByteArray serialize() const = 0; + virtual bool deserialize(const QByteArray& data) = 0; + + /** + * API adapter for the channel settings GET requests + */ + virtual int webapiSettingsGet( + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage) + { + (void) response; + errorMessage = "Not implemented"; return 501; + } + + /** + * API adapter for the channel settings PUT and PATCH requests + */ + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& featureSettingsKeys, + SWGSDRangel::SWGFeatureSettings& response, + QString& errorMessage) + { + (void) force; + (void) featureSettingsKeys; + (void) response; + errorMessage = "Not implemented"; return 501; + } +}; + +#endif // SDRBASE_FEATURE_FEATUREWEBAPIADAPER_H_ diff --git a/sdrbase/plugin/plugininterface.h b/sdrbase/plugin/plugininterface.h index e9194aec5..22557f206 100644 --- a/sdrbase/plugin/plugininterface.h +++ b/sdrbase/plugin/plugininterface.h @@ -33,6 +33,7 @@ class MIMOChannel; class ChannelAPI; class ChannelWebAPIAdapter; class DeviceWebAPIAdapter; +class FeatureWebAPIAdapter; class Feature; class SDRBASE_API PluginInterface { @@ -198,6 +199,13 @@ public: return nullptr; } + // any feature + + virtual FeatureWebAPIAdapter* createFeatureWebAPIAdapter() const + { + return nullptr; + } + // any device virtual void enumOriginDevices(QStringList& listedHwIds, OriginDevices& originDevices) diff --git a/sdrbase/plugin/pluginmanager.cpp b/sdrbase/plugin/pluginmanager.cpp index 2d5c3a6fc..d827939f9 100644 --- a/sdrbase/plugin/pluginmanager.cpp +++ b/sdrbase/plugin/pluginmanager.cpp @@ -360,3 +360,15 @@ const PluginInterface *PluginManager::getDevicePluginInterface(const QString& de return nullptr; } + +const PluginInterface *PluginManager::getFeaturePluginInterface(const QString& featureIdURI) const +{ + for (PluginAPI::FeatureRegistrations::const_iterator it = m_featureRegistrations.begin(); it != m_featureRegistrations.end(); ++it) + { + if (it->m_featureIdURI == featureIdURI) { + return it->m_plugin; + } + } + + return nullptr; +} diff --git a/sdrbase/plugin/pluginmanager.h b/sdrbase/plugin/pluginmanager.h index 6b8ab88e1..84a64aede 100644 --- a/sdrbase/plugin/pluginmanager.h +++ b/sdrbase/plugin/pluginmanager.h @@ -30,7 +30,6 @@ class QComboBox; class QPluginLoader; -class Preset; class Message; class MessageQueue; class DeviceAPI; @@ -95,6 +94,7 @@ public: const PluginInterface *getChannelPluginInterface(const QString& channelIdURI) const; const PluginInterface *getDevicePluginInterface(const QString& deviceId) const; + const PluginInterface *getFeaturePluginInterface(const QString& featureIdURI) const; static const QString& getFileInputDeviceId() { return m_fileInputDeviceTypeID; } static const QString& getFileOutputDeviceId() { return m_fileOutputDeviceTypeID; } diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index 6f64aea60..5fa3cf4a0 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -38945,7 +38945,11 @@ except ApiException as e:
+<<<<<<< ours Generated 2020-09-28T13:50:10.100+02:00 +======= + Generated 2020-09-23T23:57:22.462+02:00 +>>>>>>> theirs
diff --git a/sdrbase/settings/featuresetpreset.cpp b/sdrbase/settings/featuresetpreset.cpp new file mode 100644 index 000000000..494824a05 --- /dev/null +++ b/sdrbase/settings/featuresetpreset.cpp @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "util/simpleserializer.h" + +#include "featuresetpreset.h" + +FeatureSetPreset::FeatureSetPreset() +{ + resetToDefaults(); +} + +FeatureSetPreset::FeatureSetPreset(const FeatureSetPreset& other) : + m_group(other.m_group), + m_description(other.m_description), + m_featureConfigs(other.m_featureConfigs) +{} + +void FeatureSetPreset::resetToDefaults() +{ + m_group = "default"; + m_description = "no name"; + m_featureConfigs.clear(); +} + +QByteArray FeatureSetPreset::serialize() const +{ + SimpleSerializer s(1); + + s.writeString(1, m_group); + s.writeString(2, m_description); + s.writeS32(100, m_featureConfigs.size()); + + for(int i = 0; i < m_featureConfigs.size(); i++) + { + s.writeString(101 + i * 2, m_featureConfigs[i].m_featureIdURI); + s.writeBlob(102 + i * 2, m_featureConfigs[i].m_config); + } + + return s.final(); +} + +bool FeatureSetPreset::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if (!d.isValid()) + { + resetToDefaults(); + return false; + } + + if (d.getVersion() == 1) + { + bool tmpBool; + int tmp; + + d.readString(1, &m_group, "default"); + d.readString(2, &m_description, "no name"); + + qint32 featureCount; + d.readS32(100, &featureCount, 0); + + m_featureConfigs.clear(); + + for (int i = 0; i < featureCount; i++) + { + QString feature; + QByteArray config; + + d.readString(101 + i * 2, &feature, "unknown-feature"); + d.readBlob(102 + i * 2, &config); + m_featureConfigs.append(FeatureConfig(feature, config)); + } + + return true; + } + else + { + resetToDefaults(); + return false; + } +} diff --git a/sdrbase/settings/featuresetpreset.h b/sdrbase/settings/featuresetpreset.h new file mode 100644 index 000000000..398488e3e --- /dev/null +++ b/sdrbase/settings/featuresetpreset.h @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRBASE_SETTINGS_FEATURESETSETTINGS_H_ +#define SDRBASE_SETTINGS_FEATURESETSETTINGS_H_ + +#include +#include +#include +#include + +#include "export.h" + +class SDRBASE_API FeatureSetPreset { +public: + struct FeatureConfig { + QString m_featureIdURI; //!< Channel type ID in URI form + QByteArray m_config; + + FeatureConfig(const QString& featureIdURI, const QByteArray& config) : + m_featureIdURI(featureIdURI), + m_config(config) + { } + }; + typedef QList FeatureConfigs; + + FeatureSetPreset(); + FeatureSetPreset(const FeatureSetPreset& other); + void resetToDefaults(); + + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + + void setGroup(const QString& group) { m_group = group; } + const QString& getGroup() const { return m_group; } + void setDescription(const QString& description) { m_description = description; } + const QString& getDescription() const { return m_description; } + + void clearFeatures() { m_featureConfigs.clear(); } + void addFeature(const QString& feature, const QByteArray& config) { m_featureConfigs.append(FeatureConfig(feature, config)); } + int getFeatureCount() const { return m_featureConfigs.count(); } + const FeatureConfig& getFeatureConfig(int index) const { return m_featureConfigs.at(index); } + + static bool presetCompare(const FeatureSetPreset *p1, FeatureSetPreset *p2) + { + if (p1->m_group != p2->m_group) + { + return p1->m_group < p2->m_group; + } + else + { + return p1->m_description < p2->m_description; + } + } + +private: + // group and preset description + QString m_group; + QString m_description; + // features and configurations + FeatureConfigs m_featureConfigs; +}; + +Q_DECLARE_METATYPE(const FeatureSetPreset*) +Q_DECLARE_METATYPE(FeatureSetPreset*) + +#endif // SDRBASE_SETTINGS_FEATURESETSETTINGS_H_ diff --git a/sdrbase/settings/mainsettings.cpp b/sdrbase/settings/mainsettings.cpp index e7a8fb451..51d6ad8d9 100644 --- a/sdrbase/settings/mainsettings.cpp +++ b/sdrbase/settings/mainsettings.cpp @@ -19,15 +19,20 @@ MainSettings::MainSettings() : MainSettings::~MainSettings() { - for(int i = 0; i < m_presets.count(); ++i) + for (int i = 0; i < m_presets.count(); ++i) { delete m_presets[i]; } - for(int i = 0; i < m_commands.count(); ++i) + for (int i = 0; i < m_commands.count(); ++i) { delete m_commands[i]; } + + for (int i = 0; i < m_featureSetPresets.count(); ++i) + { + delete m_featureSetPresets[i]; + } } QString MainSettings::getFileLocation() const @@ -42,12 +47,13 @@ int MainSettings::getFileFormat() const return (int) s.format(); } -void MainSettings::load(PluginManager *pluginManager) +void MainSettings::load() { QSettings s; m_preferences.deserialize(qUncompress(QByteArray::fromBase64(s.value("preferences").toByteArray()))); m_workingPreset.deserialize(qUncompress(QByteArray::fromBase64(s.value("current").toByteArray()))); + m_workingFeatureSetPreset.deserialize(qUncompress(QByteArray::fromBase64(s.value("current-featureset").toByteArray()))); if (m_audioDeviceManager) { m_audioDeviceManager->deserialize(qUncompress(QByteArray::fromBase64(s.value("audio").toByteArray()))); @@ -59,14 +65,14 @@ void MainSettings::load(PluginManager *pluginManager) QStringList groups = s.childGroups(); - for(int i = 0; i < groups.size(); ++i) + for (int i = 0; i < groups.size(); ++i) { if (groups[i].startsWith("preset")) { s.beginGroup(groups[i]); Preset* preset = new Preset; - if(preset->deserialize(qUncompress(QByteArray::fromBase64(s.value("data").toByteArray())))) + if (preset->deserialize(qUncompress(QByteArray::fromBase64(s.value("data").toByteArray())))) { m_presets.append(preset); } @@ -82,7 +88,7 @@ void MainSettings::load(PluginManager *pluginManager) s.beginGroup(groups[i]); Command* command = new Command; - if(command->deserialize(qUncompress(QByteArray::fromBase64(s.value("data").toByteArray())))) + if (command->deserialize(qUncompress(QByteArray::fromBase64(s.value("data").toByteArray())))) { m_commands.append(command); } @@ -91,6 +97,22 @@ void MainSettings::load(PluginManager *pluginManager) delete command; } + s.endGroup(); + } + else if (groups[i].startsWith("featureset")) + { + s.beginGroup(groups[i]); + FeatureSetPreset* featureSetPreset = new FeatureSetPreset; + + if (featureSetPreset->deserialize(qUncompress(QByteArray::fromBase64(s.value("data").toByteArray())))) + { + m_featureSetPresets.append(featureSetPreset); + } + else + { + delete featureSetPreset; + } + s.endGroup(); } } @@ -99,12 +121,13 @@ void MainSettings::load(PluginManager *pluginManager) m_limeRFEUSBCalib.deserialize(qUncompress(QByteArray::fromBase64(s.value("limeRFEUSBCalib").toByteArray()))); } -void MainSettings::save(PluginManager *pluginManager) const +void MainSettings::save() const { QSettings s; s.setValue("preferences", qCompress(m_preferences.serialize()).toBase64()); s.setValue("current", qCompress(m_workingPreset.serialize()).toBase64()); + s.setValue("current-featureset", qCompress(m_workingFeatureSetPreset.serialize()).toBase64()); if (m_audioDeviceManager) { s.setValue("audio", qCompress(m_audioDeviceManager->serialize()).toBase64()); @@ -140,6 +163,14 @@ void MainSettings::save(PluginManager *pluginManager) const s.endGroup(); } + for (int i = 0; i < m_featureSetPresets.count(); ++i) + { + QString group = QString("featureset-%1").arg(i + 1); + s.beginGroup(group); + s.setValue("data", qCompress(m_featureSetPresets[i]->serialize()).toBase64()); + s.endGroup(); + } + s.setValue("hwDeviceUserArgs", qCompress(m_hardwareDeviceUserArgs.serialize()).toBase64()); s.setValue("limeRFEUSBCalib", qCompress(m_limeRFEUSBCalib.serialize()).toBase64()); } @@ -149,14 +180,18 @@ void MainSettings::initialize() resetToDefaults(); clearCommands(); clearPresets(); + clearFeatureSetPresets(); } void MainSettings::resetToDefaults() { m_preferences.resetToDefaults(); m_workingPreset.resetToDefaults(); + m_workingFeatureSetPreset.resetToDefaults(); } +// DeviceSet presets + Preset* MainSettings::newPreset(const QString& group, const QString& description) { Preset* preset = new Preset(); @@ -230,7 +265,7 @@ const Preset* MainSettings::getPreset(const QString& groupName, quint64 centerFr } } - return 0; + return nullptr; } void MainSettings::clearPresets() @@ -242,6 +277,8 @@ void MainSettings::clearPresets() m_presets.clear(); } +// Commands + void MainSettings::addCommand(Command *command) { m_commands.append(command); @@ -299,7 +336,7 @@ const Command* MainSettings::getCommand(const QString& groupName, const QString& } } - return 0; + return nullptr; } void MainSettings::clearCommands() @@ -310,3 +347,83 @@ void MainSettings::clearCommands() m_commands.clear(); } + +// FeatureSet presets + +FeatureSetPreset* MainSettings::newFeatureSetPreset(const QString& group, const QString& description) +{ + FeatureSetPreset* preset = new FeatureSetPreset(); + preset->setGroup(group); + preset->setDescription(description); + addFeatureSetPreset(preset); + return preset; +} + +void MainSettings::addFeatureSetPreset(FeatureSetPreset *preset) +{ + m_featureSetPresets.append(preset); +} + +void MainSettings::deleteFeatureSetPreset(const FeatureSetPreset* preset) +{ + m_featureSetPresets.removeAll((FeatureSetPreset*) preset); + delete (FeatureSetPreset*) preset; +} + +void MainSettings::deleteFeatureSetPresetGroup(const QString& groupName) +{ + FeatureSetPresets::iterator it = m_featureSetPresets.begin(); + + while (it != m_featureSetPresets.end()) + { + if ((*it)->getGroup() == groupName) { + it = m_featureSetPresets.erase(it); + } else { + ++it; + } + } +} + +void MainSettings::sortFeatureSetPresets() +{ + std::sort(m_featureSetPresets.begin(), m_featureSetPresets.end(), FeatureSetPreset::presetCompare); +} + +void MainSettings::renameFeatureSetPresetGroup(const QString& oldGroupName, const QString& newGroupName) +{ + int nbPresets = getFeatureSetPresetCount(); + + for (int i = 0; i < nbPresets; i++) + { + if (getPreset(i)->getGroup() == oldGroupName) + { + FeatureSetPreset *preset_mod = const_cast(getFeatureSetPreset(i)); + preset_mod->setGroup(newGroupName); + } + } +} + +const FeatureSetPreset* MainSettings::getFeatureSetPreset(const QString& groupName, const QString& description) const +{ + int nbPresets = getFeatureSetPresetCount(); + + for (int i = 0; i < nbPresets; i++) + { + if ((getFeatureSetPreset(i)->getGroup() == groupName) && + (getFeatureSetPreset(i)->getDescription() == description)) + { + return getFeatureSetPreset(i); + } + } + + return nullptr; +} + +void MainSettings::clearFeatureSetPresets() +{ + foreach (FeatureSetPreset *preset, m_featureSetPresets) { + delete preset; + } + + m_featureSetPresets.clear(); +} diff --git a/sdrbase/settings/mainsettings.h b/sdrbase/settings/mainsettings.h index 5090ba5ed..0815d5fa0 100644 --- a/sdrbase/settings/mainsettings.h +++ b/sdrbase/settings/mainsettings.h @@ -6,6 +6,7 @@ #include "limerfe/limerfeusbcalib.h" #include "preferences.h" #include "preset.h" +#include "featuresetpreset.h" #include "export.h" #include "plugin/pluginmanager.h" @@ -19,8 +20,8 @@ public: MainSettings(); ~MainSettings(); - void load(PluginManager *pluginManager); - void save(PluginManager *pluginManager) const; + void load(); + void save() const; void resetToDefaults(); void initialize(); @@ -40,6 +41,8 @@ public: void renamePresetGroup(const QString& oldGroupName, const QString& newGroupName); void deletePresetGroup(const QString& groupName); void clearPresets(); + const Preset& getWorkingPresetConst() const { return m_workingPreset; } + Preset* getWorkingPreset() { return &m_workingPreset; } void addCommand(Command *command); void deleteCommand(const Command* command); @@ -51,8 +54,20 @@ public: void deleteCommandGroup(const QString& groupName); void clearCommands(); - const Preset& getWorkingPresetConst() const { return m_workingPreset; } - Preset* getWorkingPreset() { return &m_workingPreset; } + FeatureSetPreset* newFeatureSetPreset(const QString& group, const QString& description); + void addFeatureSetPreset(FeatureSetPreset *preset); + void deleteFeatureSetPreset(const FeatureSetPreset* preset); + int getFeatureSetPresetCount() const { return m_featureSetPresets.count(); } + const FeatureSetPreset* getFeatureSetPreset(int index) const { return m_featureSetPresets[index]; } + const FeatureSetPreset* getFeatureSetPreset(const QString& groupName, const QString& description) const; + void sortFeatureSetPresets(); + void renameFeatureSetPresetGroup(const QString& oldGroupName, const QString& newGroupName); + void deleteFeatureSetPresetGroup(const QString& groupName); + void clearFeatureSetPresets(); + const FeatureSetPreset& getWorkingFeatureSetPresetConst() const { return m_workingFeatureSetPreset; } + FeatureSetPreset* getWorkingFeatureSetPreset() { return &m_workingFeatureSetPreset; } + QList *getFeatureSetPresets() { return &m_featureSetPresets; } + int getSourceIndex() const { return m_preferences.getSourceIndex(); } void setSourceIndex(int value) { m_preferences.setSourceIndex(value); } const QString& getSourceDeviceId() const { return m_preferences.getSourceDevice(); } @@ -82,10 +97,13 @@ protected: Preferences m_preferences; AudioDeviceManager *m_audioDeviceManager; Preset m_workingPreset; + FeatureSetPreset m_workingFeatureSetPreset; typedef QList Presets; Presets m_presets; typedef QList Commands; Commands m_commands; + typedef QList FeatureSetPresets; + FeatureSetPresets m_featureSetPresets; DeviceUserArgs m_hardwareDeviceUserArgs; LimeRFEUSBCalib m_limeRFEUSBCalib; AMBEEngine *m_ambeEngine; diff --git a/sdrbase/webapi/webapiadapterbase.cpp b/sdrbase/webapi/webapiadapterbase.cpp index 603c9fe89..e25051ccb 100644 --- a/sdrbase/webapi/webapiadapterbase.cpp +++ b/sdrbase/webapi/webapiadapterbase.cpp @@ -22,6 +22,8 @@ #include "channel/channelutils.h" #include "device/devicewebapiadapter.h" #include "device/deviceutils.h" +#include "feature/featurewebapiadapter.h" +#include "feature/featureutils.h" #include "dsp/glspectrumsettings.h" #include "webapiadapterbase.h" @@ -31,6 +33,8 @@ WebAPIAdapterBase::WebAPIAdapterBase() WebAPIAdapterBase::~WebAPIAdapterBase() { m_webAPIChannelAdapters.flush(); + m_webAPIFeatureAdapters.flush(); + m_webAPIDeviceAdapters.flush(); } void WebAPIAdapterBase::webapiFormatPreferences( @@ -90,6 +94,37 @@ void WebAPIAdapterBase::webapiUpdatePreferences( } } +void WebAPIAdapterBase::webapiFormatFeatureSetPreset( + SWGSDRangel::SWGFeatureSetPreset *apiPreset, + const FeatureSetPreset& preset +) +{ + apiPreset->init(); + apiPreset->setGroup(new QString(preset.getGroup())); + apiPreset->setDescription(new QString(preset.getDescription())); + + int nbFeatures = preset.getFeatureCount(); + for (int i = 0; i < nbFeatures; i++) + { + const FeatureSetPreset::FeatureConfig& featureConfig = preset.getFeatureConfig(i); + QList *swgFeatureConfigs = apiPreset->getFeatureConfigs(); + swgFeatureConfigs->append(new SWGSDRangel::SWGFeatureConfig); + swgFeatureConfigs->back()->init(); + swgFeatureConfigs->back()->setFeatureIdUri(new QString(featureConfig.m_featureIdURI)); + const QByteArray& featureSettings = featureConfig.m_config; + SWGSDRangel::SWGFeatureSettings *swgFeatureSettings = swgFeatureConfigs->back()->getConfig(); + swgFeatureSettings->init(); + FeatureWebAPIAdapter *featureWebAPIAdapter = m_webAPIFeatureAdapters.getFeatureWebAPIAdapter(featureConfig.m_featureIdURI, m_pluginManager); + + if (featureWebAPIAdapter) + { + featureWebAPIAdapter->deserialize(featureSettings); + QString errorMessage; + featureWebAPIAdapter->webapiSettingsGet(*swgFeatureSettings, errorMessage); + } + } +} + void WebAPIAdapterBase::webapiFormatPreset( SWGSDRangel::SWGPreset *apiPreset, const Preset& preset @@ -179,6 +214,65 @@ void WebAPIAdapterBase::webapiFormatPreset( apiPreset->setLayout(new QString(preset.getLayout().toBase64().toStdString().c_str())); } +void WebAPIAdapterBase::webapiUpdateFeatureSetPreset( + bool force, + SWGSDRangel::SWGFeatureSetPreset *apiPreset, + const WebAPIAdapterInterface::FeatureSetPresetKeys& presetKeys, + FeatureSetPreset *preset +) +{ + if (presetKeys.m_keys.contains("description")) { + preset->setDescription(*apiPreset->getDescription()); + } + if (presetKeys.m_keys.contains("group")) { + preset->setGroup(*apiPreset->getGroup()); + } + + if (force) { // PUT replaces feature list possibly erasing it if no features are given + preset->clearFeatures(); + } + + QList::const_iterator featureKeysIt = presetKeys.m_featureKeys.begin(); + int i = 0; + QString errorMessage; + + for (; featureKeysIt != presetKeys.m_featureKeys.end(); ++featureKeysIt, i++) + { + SWGSDRangel::SWGFeatureConfig *swgFeatureConfig = apiPreset->getFeatureConfigs()->at(i); + + if (!swgFeatureConfig) { // safety measure but should not happen + continue; + } + + if (featureKeysIt->m_keys.contains("featureIdURI")) + { + QString *featureIdURI = swgFeatureConfig->getFeatureIdUri(); + + if (!featureIdURI) { + continue; + } + + FeatureWebAPIAdapter *featureWebAPIAdapter = m_webAPIFeatureAdapters.getFeatureWebAPIAdapter(*featureIdURI, m_pluginManager); + + if (!featureWebAPIAdapter) { + continue; + } + + SWGSDRangel::SWGFeatureSettings *featureSettings = swgFeatureConfig->getConfig(); + + featureWebAPIAdapter->webapiSettingsPutPatch( + true, // features are always appended + featureKeysIt->m_featureKeys, + *featureSettings, + errorMessage + ); + + QByteArray config = featureWebAPIAdapter->serialize(); + preset->addFeature(*featureIdURI, config); + } + } +} + void WebAPIAdapterBase::webapiUpdatePreset( bool force, SWGSDRangel::SWGPreset *apiPreset, @@ -489,3 +583,39 @@ void WebAPIAdapterBase::WebAPIDeviceAdapters::flush() m_webAPIDeviceAdapters.clear(); } + +FeatureWebAPIAdapter *WebAPIAdapterBase::WebAPIFeatureAdapters::getFeatureWebAPIAdapter(const QString& featureURI, const PluginManager *pluginManager) +{ + QString registeredFeatureURI = FeatureUtils::getRegisteredFeatureURI(featureURI); + QMap::iterator it = m_webAPIFeatureAdapters.find(registeredFeatureURI); + + if (it == m_webAPIFeatureAdapters.end()) + { + const PluginInterface *pluginInterface = pluginManager->getFeaturePluginInterface(registeredFeatureURI); + + if (pluginInterface) + { + FeatureWebAPIAdapter *featureAPI = pluginInterface->createFeatureWebAPIAdapter(); + m_webAPIFeatureAdapters.insert(registeredFeatureURI, featureAPI); + return featureAPI; + } + else + { + m_webAPIFeatureAdapters.insert(registeredFeatureURI, nullptr); + return nullptr; + } + } + else + { + return *it; + } +} + +void WebAPIAdapterBase::WebAPIFeatureAdapters::flush() +{ + foreach(FeatureWebAPIAdapter *featureAPI, m_webAPIFeatureAdapters) { + delete featureAPI; + } + + m_webAPIFeatureAdapters.clear(); +} diff --git a/sdrbase/webapi/webapiadapterbase.h b/sdrbase/webapi/webapiadapterbase.h index 99937205e..a5a942f66 100644 --- a/sdrbase/webapi/webapiadapterbase.h +++ b/sdrbase/webapi/webapiadapterbase.h @@ -24,9 +24,11 @@ #include "export.h" #include "SWGPreferences.h" #include "SWGPreset.h" +#include "SWGFeatureSetPreset.h" #include "SWGCommand.h" #include "settings/preferences.h" #include "settings/preset.h" +#include "settings/featuresetpreset.h" #include "settings/mainsettings.h" #include "commands/command.h" #include "webapiadapterinterface.h" @@ -54,6 +56,10 @@ public: SWGSDRangel::SWGPreset *apiPreset, const Preset& preset ); + void webapiFormatFeatureSetPreset( + SWGSDRangel::SWGFeatureSetPreset *apiPreset, + const FeatureSetPreset& preset + ); static void webapiFormatCommand( SWGSDRangel::SWGCommand *apiCommand, const Command& command @@ -72,6 +78,12 @@ public: const WebAPIAdapterInterface::PresetKeys& presetKeys, Preset *preset ); + void webapiUpdateFeatureSetPreset( + bool force, + SWGSDRangel::SWGFeatureSetPreset *apiPreset, + const WebAPIAdapterInterface::FeatureSetPresetKeys& presetKeys, + FeatureSetPreset *preset + ); static void webapiUpdateCommand( SWGSDRangel::SWGCommand *apiCommand, const WebAPIAdapterInterface::CommandKeys& commandKeys, @@ -97,9 +109,19 @@ private: QMap m_webAPIDeviceAdapters; }; + class WebAPIFeatureAdapters + { + public: + FeatureWebAPIAdapter *getFeatureWebAPIAdapter(const QString& featureURI, const PluginManager *pluginManager); + void flush(); + private: + QMap m_webAPIFeatureAdapters; + }; + const PluginManager *m_pluginManager; WebAPIChannelAdapters m_webAPIChannelAdapters; WebAPIDeviceAdapters m_webAPIDeviceAdapters; + WebAPIFeatureAdapters m_webAPIFeatureAdapters; }; -#endif // SDRBASE_WEBAPI_WEBAPIADAPTERBASE_H_ \ No newline at end of file +#endif // SDRBASE_WEBAPI_WEBAPIADAPTERBASE_H_ diff --git a/sdrbase/webapi/webapiadapterinterface.cpp b/sdrbase/webapi/webapiadapterinterface.cpp index 83b6484e9..850fff7f5 100644 --- a/sdrbase/webapi/webapiadapterinterface.cpp +++ b/sdrbase/webapi/webapiadapterinterface.cpp @@ -110,10 +110,34 @@ void WebAPIAdapterInterface::ConfigKeys::debug() const qDebug(" }"); } + qDebug("featuresets:"); + foreach(FeatureSetPresetKeys presetKeys, m_featureSetPresetKeys) + { + qDebug(" {"); + foreach(QString presetKey, presetKeys.m_keys) { + qDebug(" %s", qPrintable(presetKey)); + } + qDebug(" featureConfigs"); + foreach(FeatureKeys featureKeys, presetKeys.m_featureKeys) + { + qDebug(" {"); + qDebug(" config:"); + foreach(QString featureKey, featureKeys.m_featureKeys) { + qDebug(" %s", qPrintable(featureKey)); + } + qDebug(" }"); + } + qDebug(" }"); + } + qDebug("workingPreset:"); foreach(QString presetKey, m_workingPresetKeys.m_keys) { qDebug(" %s", qPrintable(presetKey)); } + qDebug("workingFeatureSetPreset:"); + foreach(QString presetKey, m_workingFeatureSetPresetKeys.m_keys) { + qDebug(" %s", qPrintable(presetKey)); + } qDebug(" spectrumConfig:"); foreach(QString spectrumKey, m_workingPresetKeys.m_spectrumKeys) { qDebug(" %s", qPrintable(spectrumKey)); diff --git a/sdrbase/webapi/webapiadapterinterface.h b/sdrbase/webapi/webapiadapterinterface.h index f5fe01fc8..73c4de8a0 100644 --- a/sdrbase/webapi/webapiadapterinterface.h +++ b/sdrbase/webapi/webapiadapterinterface.h @@ -82,6 +82,11 @@ public: QStringList m_keys; QStringList m_deviceKeys; }; + struct FeatureKeys + { + QStringList m_keys; + QStringList m_featureKeys; + }; struct PresetKeys { QStringList m_keys; @@ -89,6 +94,12 @@ public: QList m_channelsKeys; QList m_devicesKeys; }; + struct FeatureSetPresetKeys + { + QStringList m_keys; + QList m_featureKeys; + QList m_devicesKeys; + }; struct CommandKeys { QStringList m_keys; @@ -97,7 +108,9 @@ public: { QStringList m_preferencesKeys; PresetKeys m_workingPresetKeys; + FeatureSetPresetKeys m_workingFeatureSetPresetKeys; QList m_presetKeys; + QList m_featureSetPresetKeys; QList m_commandKeys; void debug() const; }; diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index 499e4b28d..d7f32faba 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -51,6 +51,10 @@ #include "SWGChannelActions.h" #include "SWGSuccessResponse.h" #include "SWGErrorResponse.h" +#include "SWGFeatureSetList.h" +#include "SWGFeatureSettings.h" +#include "SWGFeatureReport.h" +#include "SWGFeatureActions.h" const QMap WebAPIRequestMapper::m_channelURIToSettingsKey = { {"sdrangel.channel.amdemod", "AMDemodSettings"}, @@ -229,6 +233,10 @@ const QMap WebAPIRequestMapper::m_mimoDeviceHwIdToSettingsKey= const QMap WebAPIRequestMapper::m_mimoDeviceHwIdToActionsKey= { }; +const QMap WebAPIRequestMapper::m_featureURIToSettingsKey = { + {"sdrangel.feature.simpleptt", "SimplePTTSettings"} +}; + WebAPIRequestMapper::WebAPIRequestMapper(QObject* parent) : HttpRequestHandler(parent), m_adapter(0) @@ -2882,7 +2890,10 @@ bool WebAPIRequestMapper::validateLimeRFEConfig(SWGSDRangel::SWGLimeRFESettings& return true; } -bool WebAPIRequestMapper::validateConfig(SWGSDRangel::SWGInstanceConfigResponse& config, QJsonObject& jsonObject, WebAPIAdapterInterface::ConfigKeys& configKeys) +bool WebAPIRequestMapper::validateConfig( + SWGSDRangel::SWGInstanceConfigResponse& config, + QJsonObject& jsonObject, + WebAPIAdapterInterface::ConfigKeys& configKeys) { if (jsonObject.contains("preferences")) { @@ -2927,6 +2938,23 @@ bool WebAPIRequestMapper::validateConfig(SWGSDRangel::SWGInstanceConfigResponse& } } + if (jsonObject.contains("featuresetpresets")) + { + QList *featureSetPresets = new QList(); + config.setFeaturesetpresets(featureSetPresets); + QJsonArray presetsJson = jsonObject["featuresetpresets"].toArray(); + QJsonArray::const_iterator featureSetPresetsIt = presetsJson.begin(); + + for (; featureSetPresetsIt != presetsJson.end(); ++featureSetPresetsIt) + { + QJsonObject presetJson = featureSetPresetsIt->toObject(); + SWGSDRangel::SWGFeatureSetPreset *featureSetPreset = new SWGSDRangel::SWGFeatureSetPreset(); + featureSetPresets->append(featureSetPreset); + configKeys.m_featureSetPresetKeys.append(WebAPIAdapterInterface::FeatureSetPresetKeys()); + appendFeatureSetPresetKeys(featureSetPreset, presetJson, configKeys.m_featureSetPresetKeys.back()); + } + } + if (jsonObject.contains("workingPreset")) { SWGSDRangel::SWGPreset *preset = new SWGSDRangel::SWGPreset(); @@ -2935,6 +2963,58 @@ bool WebAPIRequestMapper::validateConfig(SWGSDRangel::SWGInstanceConfigResponse& appendPresetKeys(preset, presetJson, configKeys.m_workingPresetKeys); } + if (jsonObject.contains("workingFeatureSetPreset")) + { + SWGSDRangel::SWGFeatureSetPreset *preset = new SWGSDRangel::SWGFeatureSetPreset(); + config.setWorkingFeatureSetPreset(preset); + QJsonObject presetJson = jsonObject["workingFeatureSetPreset"].toObject(); + appendFeatureSetPresetKeys(preset, presetJson, configKeys.m_workingFeatureSetPresetKeys); + } + + return true; +} + +bool WebAPIRequestMapper::appendFeatureSetPresetKeys( + SWGSDRangel::SWGFeatureSetPreset *preset, + const QJsonObject& presetJson, + WebAPIAdapterInterface::FeatureSetPresetKeys& featureSetPresetKeys +) +{ + if (presetJson.contains("description")) + { + preset->setDescription(new QString(presetJson["description"].toString())); + featureSetPresetKeys.m_keys.append("description"); + } + if (presetJson.contains("group")) + { + preset->setGroup(new QString(presetJson["group"].toString())); + featureSetPresetKeys.m_keys.append("group"); + } + if (presetJson.contains("featureConfigs")) + { + QJsonArray featuresJson = presetJson["featureConfigs"].toArray(); + QJsonArray::const_iterator featuresIt = featuresJson.begin(); + QList *features = new QList(); + preset->setFeatureConfigs(features); + + for (; featuresIt != featuresJson.end(); ++featuresIt) + { + QJsonObject featureJson = featuresIt->toObject(); + SWGSDRangel::SWGFeatureConfig *featureConfig = new SWGSDRangel::SWGFeatureConfig(); + featureSetPresetKeys.m_featureKeys.append(WebAPIAdapterInterface::FeatureKeys()); + + if (appendPresetFeatureKeys(featureConfig, featureJson, featureSetPresetKeys.m_featureKeys.back())) + { + features->append(featureConfig); + } + else + { + delete featureConfig; + featureSetPresetKeys.m_featureKeys.takeLast(); // remove channel keys + } + } + } + return true; } @@ -3042,6 +3122,40 @@ bool WebAPIRequestMapper::appendPresetKeys( return true; } +bool WebAPIRequestMapper::appendPresetFeatureKeys( + SWGSDRangel::SWGFeatureConfig *feature, + const QJsonObject& featureSettingsJson, + WebAPIAdapterInterface::FeatureKeys& featureKeys +) +{ + if (featureSettingsJson.contains("featureIdURI")) + { + QString *featureURI = new QString(featureSettingsJson["featureIdURI"].toString()); + feature->setFeatureIdUri(featureURI); + featureKeys.m_keys.append("featureIdURI"); + + if (featureSettingsJson.contains("config") && m_featureURIToSettingsKey.contains(*featureURI)) + { + SWGSDRangel::SWGFeatureSettings *featureSettings = new SWGSDRangel::SWGFeatureSettings(); + feature->setConfig(featureSettings); + return getFeatureSettings( + m_channelURIToSettingsKey[*featureURI], + featureSettings, + featureSettingsJson["config"].toObject(), + featureKeys.m_featureKeys + ); + } + else + { + return false; + } + } + else + { + return false; + } +} + bool WebAPIRequestMapper::appendPresetChannelKeys( SWGSDRangel::SWGChannelConfig *channel, const QJsonObject& channelSettingsJson, @@ -3522,6 +3636,70 @@ bool WebAPIRequestMapper::getDeviceActions( } +bool WebAPIRequestMapper::getFeatureSettings( + const QString& featureSettingsKey, + SWGSDRangel::SWGFeatureSettings *featureSettings, + const QJsonObject& featureSettingsJson, + QStringList& featureSettingsKeys +) +{ + QStringList featureKeys = featureSettingsJson.keys(); + + if (featureKeys.contains(featureSettingsKey) && featureSettingsJson[featureSettingsKey].isObject()) + { + QJsonObject settingsJsonObject = featureSettingsJson[featureSettingsKey].toObject(); + featureSettingsKeys = settingsJsonObject.keys(); + + if (featureSettingsKey == "SimplePTTSettings") + { + featureSettings->setSimplePttSettings(new SWGSDRangel::SWGSimplePTTSettings()); + featureSettings->getSimplePttSettings()->fromJsonObject(settingsJsonObject); + } + else + { + return false; + } + + return true; + } + else + { + return false; + } +} + +bool WebAPIRequestMapper::getFeatureActions( + const QString& featureActionsKey, + SWGSDRangel::SWGFeatureActions *featureActions, + const QJsonObject& featureActionsJson, + QStringList& featureActionsKeys +) +{ + QStringList featureKeys = featureActionsJson.keys(); + + if (featureKeys.contains(featureActionsKey) && featureActionsJson[featureActionsKey].isObject()) + { + QJsonObject actionsJsonObject = featureActionsJson[featureActionsKey].toObject(); + featureActionsKeys = actionsJsonObject.keys(); + + if (featureActionsKey == "SimplePTTActions") + { + featureActions->setSimplePttActions(new SWGSDRangel::SWGSimplePTTActions()); + featureActions->getSimplePttActions()->fromJsonObject(actionsJsonObject); + } + else + { + return false; + } + + return true; + } + else + { + return false; + } +} + void WebAPIRequestMapper::appendSettingsSubKeys( const QJsonObject& parentSettingsJsonObject, QJsonObject& childSettingsJsonObject, @@ -4061,4 +4239,4 @@ void WebAPIRequestMapper::processSoapySDRSettings( appendSettingsArrayKeys(deviceSettingsJson, "tunableElements", deviceSettingsKeys); } -} \ No newline at end of file +} diff --git a/sdrbase/webapi/webapirequestmapper.h b/sdrbase/webapi/webapirequestmapper.h index 5d981dbab..7a252b5bb 100644 --- a/sdrbase/webapi/webapirequestmapper.h +++ b/sdrbase/webapi/webapirequestmapper.h @@ -37,6 +37,9 @@ namespace SWGSDRangel class SWGPreset; class SWGChannelConfig; class SWGDeviceConfig; + class SWGFeatureConfig; + class SWGFeatureActions; + class SWGFeatureSetPreset; } class SDRBASE_API WebAPIRequestMapper : public qtwebapp::HttpRequestHandler { @@ -104,11 +107,23 @@ private: bool validateLimeRFEConfig(SWGSDRangel::SWGLimeRFESettings& limeRFESettings, QJsonObject& jsonObject, QStringList& limeRFESettingsKeys); bool validateConfig(SWGSDRangel::SWGInstanceConfigResponse& config, QJsonObject& jsonObject, WebAPIAdapterInterface::ConfigKeys& configKeys); + bool appendFeatureSetPresetKeys( + SWGSDRangel::SWGFeatureSetPreset *preset, + const QJsonObject& presetJson, + WebAPIAdapterInterface::FeatureSetPresetKeys& featureSetPresetKeys + ); + bool appendPresetKeys( SWGSDRangel::SWGPreset *preset, const QJsonObject& presetJson, WebAPIAdapterInterface::PresetKeys& presetKeys); + bool appendPresetFeatureKeys( + SWGSDRangel::SWGFeatureConfig *feature, + const QJsonObject& featureSettingsJson, + WebAPIAdapterInterface::FeatureKeys& featureKeys + ); + bool appendPresetChannelKeys( SWGSDRangel::SWGChannelConfig *channel, const QJsonObject& channelSettngsJson, @@ -160,6 +175,20 @@ private: const QString& parentKey, QStringList& keyList); + bool getFeatureSettings( + const QString& featureSettingsKey, + SWGSDRangel::SWGFeatureSettings *featureSettings, + const QJsonObject& featureSettingsJson, + QStringList& featureSettingsKeys + ); + + bool getFeatureActions( + const QString& featureActionsKey, + SWGSDRangel::SWGFeatureActions *featureActions, + const QJsonObject& featureActionsJson, + QStringList& featureSettingsKeys + ); + bool parseJsonBody(QString& jsonStr, QJsonObject& jsonObject, qtwebapp::HttpResponse& response); void resetDeviceSettings(SWGSDRangel::SWGDeviceSettings& deviceSettings); @@ -194,6 +223,9 @@ private: static const QMap m_sourceDeviceHwIdToActionsKey; static const QMap m_sinkDeviceHwIdToActionsKey; static const QMap m_mimoDeviceHwIdToActionsKey; + static const QMap m_featureTypeToSettingsKey; + static const QMap m_featureTypeToActionsKey; + static const QMap m_featureURIToSettingsKey; }; #endif /* SDRBASE_WEBAPI_WEBAPIREQUESTMAPPER_H_ */ diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index 76d4523f4..fda94c201 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -34,6 +34,7 @@ set(sdrgui_SOURCES gui/fmpreemphasisdialog.cpp gui/featureadddialog.cpp gui/featuresdock.cpp + gui/featurepresetsdialog.cpp gui/featurewindow.cpp gui/glscope.cpp gui/glscopegui.cpp @@ -115,6 +116,7 @@ set(sdrgui_HEADERS gui/fmpreemphasisdialog.h gui/featureadddialog.h gui/featuresdock.h + gui/featurepresetsdialog.h gui/featurewindow.h gui/glscope.h gui/glscopegui.h diff --git a/sdrgui/feature/featureuiset.cpp b/sdrgui/feature/featureuiset.cpp index 85a8673ab..a79f41d41 100644 --- a/sdrgui/feature/featureuiset.cpp +++ b/sdrgui/feature/featureuiset.cpp @@ -17,6 +17,9 @@ #include "gui/featurewindow.h" #include "plugin/plugininstancegui.h" +#include "plugin/pluginapi.h" +#include "settings/featuresetpreset.h" +#include "feature/featureutils.h" #include "featureuiset.h" @@ -63,6 +66,16 @@ void FeatureUISet::renameFeatureInstances() } } +// sort by name +bool FeatureUISet::FeatureInstanceRegistration::operator<(const FeatureInstanceRegistration& other) const +{ + if (m_gui && other.m_gui) { + return m_gui->getName() < other.m_gui->getName(); + } else { + return false; + } +} + void FeatureUISet::freeFeatures() { for(int i = 0; i < m_featureInstanceRegistrations.count(); i++) @@ -101,3 +114,66 @@ Feature *FeatureUISet::getFeatureAt(int featureIndex) return nullptr; } } + +void FeatureUISet::loadFeatureSetSettings(const FeatureSetPreset *preset, PluginAPI *pluginAPI, WebAPIAdapterInterface *apiAdapter) +{ + qDebug("FeatureUISet::loadFeatureSetSettings: Loading preset [%s | %s]", qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); + + // Available feature plugins + PluginAPI::FeatureRegistrations *featureRegistrations = pluginAPI->getFeatureRegistrations(); + + // copy currently open features and clear list + FeatureInstanceRegistrations openFeatures = m_featureInstanceRegistrations; + m_featureInstanceRegistrations.clear(); + + for (int i = 0; i < openFeatures.count(); i++) + { + qDebug("FeatureUISet::loadFeatureSetSettings: destroying old feature [%s]", qPrintable(openFeatures[i].m_featureName)); + openFeatures[i].m_gui->destroy(); + } + + qDebug("FeatureUISet::loadFeatureSetSettings: %d feature(s) in preset", preset->getFeatureCount()); + + for (int i = 0; i < preset->getFeatureCount(); i++) + { + const FeatureSetPreset::FeatureConfig& featureConfig = preset->getFeatureConfig(i); + FeatureInstanceRegistration reg; + + // create feature instance + + for(int i = 0; i < featureRegistrations->count(); i++) + { + if (FeatureUtils::compareFeatureURIs((*featureRegistrations)[i].m_featureIdURI, featureConfig.m_featureIdURI)) + { + qDebug("FeatureUISet::loadFeatureSetSettings: creating new feature [%s] from config [%s]", + qPrintable((*featureRegistrations)[i].m_featureIdURI), + qPrintable(featureConfig.m_featureIdURI)); + Feature *feature = + (*featureRegistrations)[i].m_plugin->createFeature(apiAdapter); + PluginInstanceGUI *featureGUI = + (*featureRegistrations)[i].m_plugin->createFeatureGUI(this, feature); + reg = FeatureInstanceRegistration(featureConfig.m_featureIdURI, featureGUI, feature); + break; + } + } + + if (reg.m_gui) + { + qDebug("FeatureUISet::loadFeatureSetSettings: deserializing feature [%s]", qPrintable(featureConfig.m_featureIdURI)); + reg.m_gui->deserialize(featureConfig.m_config); + } + } + + renameFeatureInstances(); +} + +void FeatureUISet::saveFeatureSetSettings(FeatureSetPreset *preset) +{ + std::sort(m_featureInstanceRegistrations.begin(), m_featureInstanceRegistrations.end()); // sort by increasing delta frequency and type + + for (int i = 0; i < m_featureInstanceRegistrations.count(); i++) + { + qDebug("FeatureUISet::saveFeatureSetSettings: saving feature [%s]", qPrintable(m_featureInstanceRegistrations[i].m_featureName)); + preset->addFeature(m_featureInstanceRegistrations[i].m_featureName, m_featureInstanceRegistrations[i].m_gui->serialize()); + } +} diff --git a/sdrgui/feature/featureuiset.h b/sdrgui/feature/featureuiset.h index 7bb36e578..25a8e2dab 100644 --- a/sdrgui/feature/featureuiset.h +++ b/sdrgui/feature/featureuiset.h @@ -26,7 +26,10 @@ class QWidget; class FeatureWindow; class PluginInstanceGUI; +class PluginAPI; class Feature; +class FeatureSetPreset; +class WebAPIAdapterInterface; class SDRGUI_API FeatureUISet { @@ -42,6 +45,8 @@ public: void deleteFeature(int featureIndex); const Feature *getFeatureAt(int featureIndex) const; Feature *getFeatureAt(int featureIndex); + void loadFeatureSetSettings(const FeatureSetPreset* preset, PluginAPI *pluginAPI, WebAPIAdapterInterface *apiAdapter); + void saveFeatureSetSettings(FeatureSetPreset* preset); FeatureWindow *m_featureWindow; diff --git a/sdrgui/gui/featurepresetsdialog.cpp b/sdrgui/gui/featurepresetsdialog.cpp new file mode 100644 index 000000000..640ddcb46 --- /dev/null +++ b/sdrgui/gui/featurepresetsdialog.cpp @@ -0,0 +1,413 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 F4EXB // +// written by Edouard Griffiths // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include + +#include "gui/addpresetdialog.h" +#include "feature/featureuiset.h" +#include "featurepresetsdialog.h" +#include "settings/featuresetpreset.h" +#include "ui_featurepresetsdialog.h" + +FeaturePresetsDialog::FeaturePresetsDialog(QWidget* parent) : + QDialog(parent), + ui(new Ui::FeaturePresetsDialog), + m_featureSetPresets(nullptr) +{ + ui->setupUi(this); +} + +FeaturePresetsDialog::~FeaturePresetsDialog() +{ + delete ui; +} + +void FeaturePresetsDialog::populateTree() +{ + if (!m_featureSetPresets) { + return; + } + + QList::const_iterator it = m_featureSetPresets->begin(); + int middleIndex = m_featureSetPresets->size() / 2; + QTreeWidgetItem *treeItem; + ui->presetsTree->clear(); + + for (int i = 0; it != m_featureSetPresets->end(); ++it, i++) + { + treeItem = addPresetToTree(*it); + + if (i == middleIndex) { + ui->presetsTree->setCurrentItem(treeItem); + } + } + + updatePresetControls(); +} + +QTreeWidgetItem* FeaturePresetsDialog::addPresetToTree(const FeatureSetPreset* preset) +{ + QTreeWidgetItem* group = nullptr; + + for (int i = 0; i < ui->presetsTree->topLevelItemCount(); i++) + { + if (ui->presetsTree->topLevelItem(i)->text(0) == preset->getGroup()) + { + group = ui->presetsTree->topLevelItem(i); + break; + } + } + + if (!group) + { + QStringList sl; + sl.append(preset->getGroup()); + group = new QTreeWidgetItem(ui->presetsTree, sl, PGroup); + group->setFirstColumnSpanned(true); + group->setExpanded(true); + ui->presetsTree->sortByColumn(0, Qt::AscendingOrder); + } + + QStringList sl; + sl.append(preset->getDescription()); + QTreeWidgetItem* item = new QTreeWidgetItem(group, sl, PItem); // description column + item->setTextAlignment(0, Qt::AlignLeft); + item->setData(0, Qt::UserRole, QVariant::fromValue(preset)); + + updatePresetControls(); + return item; +} + +void FeaturePresetsDialog::updatePresetControls() +{ + ui->presetsTree->resizeColumnToContents(0); + + if (ui->presetsTree->currentItem()) + { + ui->presetDelete->setEnabled(true); + ui->presetLoad->setEnabled(true); + } + else + { + ui->presetDelete->setEnabled(false); + ui->presetLoad->setEnabled(false); + } +} + +void FeaturePresetsDialog::on_presetSave_clicked() +{ + QStringList groups; + QString group; + QString description = ""; + + for (int i = 0; i < ui->presetsTree->topLevelItemCount(); i++) { + groups.append(ui->presetsTree->topLevelItem(i)->text(0)); + } + + QTreeWidgetItem* item = ui->presetsTree->currentItem(); + + if (item) + { + if (item->type() == PGroup) + { + group = item->text(0); + } + else if (item->type() == PItem) + { + group = item->parent()->text(0); + description = item->text(0); + } + } + + AddPresetDialog dlg(groups, group, this); + + if (description.length() > 0) { + dlg.setDescription(description); + } + + if (dlg.exec() == QDialog::Accepted) + { + FeatureSetPreset* preset = newFeatureSetPreset(dlg.group(), dlg.description()); + savePresetSettings(preset); + ui->presetsTree->setCurrentItem(addPresetToTree(preset)); + } + + sortFeatureSetPresets(); +} + +void FeaturePresetsDialog::on_presetUpdate_clicked() +{ + QTreeWidgetItem* item = ui->presetsTree->currentItem(); + const FeatureSetPreset* changedPreset = nullptr; + + if (item) + { + if( item->type() == PItem) + { + const FeatureSetPreset* preset = qvariant_cast(item->data(0, Qt::UserRole)); + + if (preset) + { + FeatureSetPreset* preset_mod = const_cast(preset); + savePresetSettings(preset_mod); + changedPreset = preset; + } + } + } + + sortFeatureSetPresets(); + ui->presetsTree->clear(); + + for (int i = 0; i < m_featureSetPresets->size(); ++i) + { + QTreeWidgetItem *item_x = addPresetToTree(m_featureSetPresets->at(i)); + const FeatureSetPreset* preset_x = qvariant_cast(item_x->data(0, Qt::UserRole)); + + if (changedPreset && (preset_x == changedPreset)) { // set cursor on changed preset + ui->presetsTree->setCurrentItem(item_x); + } + } +} + +void FeaturePresetsDialog::on_settingsSave_clicked() +{ +} + +void FeaturePresetsDialog::on_presetEdit_clicked() +{ + QTreeWidgetItem* item = ui->presetsTree->currentItem(); + QStringList groups; + bool change = false; + const FeatureSetPreset *changedPreset = nullptr; + QString newGroupName; + + for (int i = 0; i < ui->presetsTree->topLevelItemCount(); i++) { + groups.append(ui->presetsTree->topLevelItem(i)->text(0)); + } + + if (item) + { + if (item->type() == PItem) + { + const FeatureSetPreset* preset = qvariant_cast(item->data(0, Qt::UserRole)); + AddPresetDialog dlg(groups, preset->getGroup(), this); + dlg.setDescription(preset->getDescription()); + + if (dlg.exec() == QDialog::Accepted) + { + FeatureSetPreset* preset_mod = const_cast(preset); + preset_mod->setGroup(dlg.group()); + preset_mod->setDescription(dlg.description()); + change = true; + changedPreset = preset; + } + } + else if (item->type() == PGroup) + { + AddPresetDialog dlg(groups, item->text(0), this); + dlg.showGroupOnly(); + dlg.setDialogTitle("Edit preset group"); + + if (dlg.exec() == QDialog::Accepted) + { + renamePresetGroup(item->text(0), dlg.group()); + newGroupName = dlg.group(); + change = true; + } + } + } + + if (change) + { + sortFeatureSetPresets(); + ui->presetsTree->clear(); + + for (int i = 0; i < m_featureSetPresets->size(); ++i) + { + QTreeWidgetItem *item_x = addPresetToTree(m_featureSetPresets->at(i)); + const FeatureSetPreset* preset_x = qvariant_cast(item_x->data(0, Qt::UserRole)); + + if (changedPreset && (preset_x == changedPreset)) { // set cursor on changed preset + ui->presetsTree->setCurrentItem(item_x); + } + } + + if (!changedPreset) // on group name change set cursor on the group that has been changed + { + for(int i = 0; i < ui->presetsTree->topLevelItemCount(); i++) + { + QTreeWidgetItem* item = ui->presetsTree->topLevelItem(i); + + if (item->text(0) == newGroupName) { + ui->presetsTree->setCurrentItem(item); + } + } + } + } +} + +void FeaturePresetsDialog::on_presetDelete_clicked() +{ + QTreeWidgetItem* item = ui->presetsTree->currentItem(); + + if (item == 0) + { + updatePresetControls(); + return; + } + else + { + if (item->type() == PItem) + { + const FeatureSetPreset* preset = qvariant_cast(item->data(0, Qt::UserRole)); + + if (preset) + { + if(QMessageBox::question(this, tr("Delete Preset"), tr("Do you want to delete preset '%1'?").arg(preset->getDescription()), QMessageBox::No | QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { + delete item; + deletePreset(preset); + } + } + } + else if (item->type() == PGroup) + { + if (QMessageBox::question(this, + tr("Delete preset group"), + tr("Do you want to delete preset group '%1'?") + .arg(item->text(0)), QMessageBox::No | QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) + { + deletePresetGroup(item->text(0)); + + ui->presetsTree->clear(); + + for (int i = 0; i < m_featureSetPresets->size(); ++i) { + addPresetToTree(m_featureSetPresets->at(i)); + } + } + } + } +} + +void FeaturePresetsDialog::on_presetLoad_clicked() +{ + qDebug() << "FeaturePresetsDialog::on_presetLoad_clicked"; + + QTreeWidgetItem* item = ui->presetsTree->currentItem(); + + if (!item) + { + qDebug("FeaturePresetsDialog::on_presetLoad_clicked: item null"); + updatePresetControls(); + return; + } + + const FeatureSetPreset* preset = qvariant_cast(item->data(0, Qt::UserRole)); + + if (!preset) + { + qDebug("FeatureSetPreset::on_presetLoad_clicked: preset null"); + return; + } + + loadPresetSettings(preset); +} + +void FeaturePresetsDialog::on_presetTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous) +{ + (void) current; + (void) previous; + updatePresetControls(); +} + +void FeaturePresetsDialog::on_presetTree_itemActivated(QTreeWidgetItem *item, int column) +{ + (void) item; + (void) column; + on_presetLoad_clicked(); +} + +FeatureSetPreset* FeaturePresetsDialog::newFeatureSetPreset(const QString& group, const QString& description) +{ + FeatureSetPreset* preset = new FeatureSetPreset(); + preset->setGroup(group); + preset->setDescription(description); + addFeatureSetPreset(preset); + return preset; +} + +void FeaturePresetsDialog::addFeatureSetPreset(FeatureSetPreset *preset) +{ + m_featureSetPresets->append(preset); +} + +void FeaturePresetsDialog::savePresetSettings(FeatureSetPreset* preset) +{ + qDebug("FeaturePresetsDialog::savePresetSettings: preset [%s | %s]", + qPrintable(preset->getGroup()), + qPrintable(preset->getDescription())); + + preset->clearFeatures(); + m_featureUISet->saveFeatureSetSettings(preset); +} + +void FeaturePresetsDialog::loadPresetSettings(const FeatureSetPreset* preset) +{ + qDebug("FeaturePresetsDialog::loadPresetSettings: preset [%s | %s]", + qPrintable(preset->getGroup()), + qPrintable(preset->getDescription())); + + m_featureUISet->loadFeatureSetSettings(preset, m_pluginAPI, m_apiAdapter); +} + +void FeaturePresetsDialog::sortFeatureSetPresets() +{ + std::sort(m_featureSetPresets->begin(), m_featureSetPresets->end(), FeatureSetPreset::presetCompare); +} + +void FeaturePresetsDialog::renamePresetGroup(const QString& oldGroupName, const QString& newGroupName) +{ + for (int i = 0; i < m_featureSetPresets->size(); i++) + { + if (m_featureSetPresets->at(i)->getGroup() == oldGroupName) + { + FeatureSetPreset *preset_mod = const_cast(m_featureSetPresets->at(i)); + preset_mod->setGroup(newGroupName); + } + } +} + +void FeaturePresetsDialog::deletePreset(const FeatureSetPreset* preset) +{ + m_featureSetPresets->removeAll((FeatureSetPreset*) preset); + delete (FeatureSetPreset*) preset; +} + +void FeaturePresetsDialog::deletePresetGroup(const QString& groupName) +{ + QList::iterator it = m_featureSetPresets->begin(); + + while (it != m_featureSetPresets->end()) + { + if ((*it)->getGroup() == groupName) { + it = m_featureSetPresets->erase(it); + } else { + ++it; + } + } +} diff --git a/sdrgui/gui/featurepresetsdialog.h b/sdrgui/gui/featurepresetsdialog.h new file mode 100644 index 000000000..49f3d7c2c --- /dev/null +++ b/sdrgui/gui/featurepresetsdialog.h @@ -0,0 +1,82 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020 F4EXB // +// written by Edouard Griffiths // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRGUI_GUI_FEATUREPRESETDIALOG_H_ +#define SDRGUI_GUI_FEATUREPRESETDIALOG_H_ + +#include +#include +#include + +#include "export.h" + +class FeatureSetPreset; +class FeatureUISet; +class WebAPIAdapterInterface; +class PluginAPI; + +namespace Ui { + class FeaturePresetsDialog; +} + +class SDRGUI_API FeaturePresetsDialog : public QDialog { + Q_OBJECT +public: + explicit FeaturePresetsDialog(QWidget* parent = nullptr); + ~FeaturePresetsDialog(); + void setPresets(QList* presets) { m_featureSetPresets = presets; } + void setFeatureUISet(FeatureUISet *featureUISet) { m_featureUISet = featureUISet; } + void setPluginAPI(PluginAPI *pluginAPI) { m_pluginAPI = pluginAPI; } + void setWebAPIAdapter(WebAPIAdapterInterface *apiAdapter) { m_apiAdapter = apiAdapter; } + void populateTree(); + +private: + enum { + PGroup, + PItem + }; + + Ui::FeaturePresetsDialog* ui; + QList *m_featureSetPresets; + FeatureUISet *m_featureUISet; + PluginAPI *m_pluginAPI; + WebAPIAdapterInterface *m_apiAdapter; + + QTreeWidgetItem* addPresetToTree(const FeatureSetPreset* preset); + void updatePresetControls(); + FeatureSetPreset* newFeatureSetPreset(const QString& group, const QString& description); + void addFeatureSetPreset(FeatureSetPreset *preset); + void savePresetSettings(FeatureSetPreset* preset); + void loadPresetSettings(const FeatureSetPreset* preset); + void sortFeatureSetPresets(); + void renamePresetGroup(const QString& oldGroupName, const QString& newGroupName); + void deletePreset(const FeatureSetPreset* preset); + void deletePresetGroup(const QString& groupName); + +private slots: + void on_presetSave_clicked(); + void on_presetUpdate_clicked(); + void on_settingsSave_clicked(); + void on_presetEdit_clicked(); + void on_presetDelete_clicked(); + void on_presetLoad_clicked(); + void on_presetTree_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous); + void on_presetTree_itemActivated(QTreeWidgetItem *item, int column); +}; + +#endif // SDRGUI_GUI_FEATUREPRESETDIALOG_H_ diff --git a/sdrgui/gui/featurepresetsdialog.ui b/sdrgui/gui/featurepresetsdialog.ui new file mode 100644 index 000000000..55de482eb --- /dev/null +++ b/sdrgui/gui/featurepresetsdialog.ui @@ -0,0 +1,261 @@ + + + FeaturePresetsDialog + + + + 0 + 0 + 392 + 414 + + + + + Liberation Sans + 9 + + + + Feature presets + + + + + 40 + 380 + 341 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + 0 + 10 + 392 + 310 + + + + + + + 5 + + + true + + + 1 + + + 5 + + + + Description + + + + + + + + + + 0 + 330 + 392 + 34 + + + + + + + Save current settings as new preset + + + ... + + + + :/create.png:/create.png + + + + 16 + 16 + + + + + + + + Update selected preset with current settings + + + ... + + + + :/recycle.png:/recycle.png + + + + 16 + 16 + + + + + + + + Save the current settings (inc. presets) + + + ... + + + + :/save.png:/save.png + + + + 16 + 16 + + + + + + + + Edit preset details + + + + + + + :/edit.png:/edit.png + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Delete selected preset + + + ... + + + + :/bin.png:/bin.png + + + + 16 + 16 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Load selected preset + + + ... + + + + :/load.png:/load.png + + + + 16 + 16 + + + + + + + + + + + + + buttonBox + accepted() + FeaturePresetsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + FeaturePresetsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/sdrgui/gui/featuresdock.cpp b/sdrgui/gui/featuresdock.cpp index cd1906de0..1f32f0634 100644 --- a/sdrgui/gui/featuresdock.cpp +++ b/sdrgui/gui/featuresdock.cpp @@ -41,6 +41,12 @@ FeaturesDock::FeaturesDock(QWidget *parent, Qt::WindowFlags flags) : m_addFeatureButton->setToolTip("Add features"); m_addFeatureButton->setFixedSize(16, 16); + m_presetsButton = new QPushButton(); + QIcon presetsIcon(":/star.png"); + m_presetsButton->setIcon(presetsIcon); + m_presetsButton->setToolTip("Feature presets"); + m_presetsButton->setFixedSize(16, 16); + m_normalButton = new QPushButton(); QIcon normalIcon = style()->standardIcon(QStyle::SP_TitleBarNormalButton, 0, this); m_normalButton->setIcon(normalIcon); @@ -52,6 +58,7 @@ FeaturesDock::FeaturesDock(QWidget *parent, Qt::WindowFlags flags) : m_closeButton->setFixedSize(12, 12); m_titleBarLayout->addWidget(m_addFeatureButton); + m_titleBarLayout->addWidget(m_presetsButton); m_titleBarLayout->addWidget(m_titleLabel); m_titleBarLayout->addWidget(m_normalButton); m_titleBarLayout->addWidget(m_closeButton); @@ -64,6 +71,13 @@ FeaturesDock::FeaturesDock(QWidget *parent, Qt::WindowFlags flags) : &FeaturesDock::addFeatureDialog ); + QObject::connect( + m_presetsButton, + &QPushButton::clicked, + this, + &FeaturesDock::presetsDialog + ); + QObject::connect( m_normalButton, &QPushButton::clicked, @@ -99,7 +113,12 @@ void FeaturesDock::toggleFloating() void FeaturesDock::addFeatureDialog() { m_featureAddDialog.exec(); +} +void FeaturesDock::presetsDialog() +{ + m_featurePresetsDialog.populateTree(); + m_featurePresetsDialog.exec(); } void FeaturesDock::addFeatureEmitted(int featureIndex) diff --git a/sdrgui/gui/featuresdock.h b/sdrgui/gui/featuresdock.h index ececba544..f50af852c 100644 --- a/sdrgui/gui/featuresdock.h +++ b/sdrgui/gui/featuresdock.h @@ -22,6 +22,7 @@ #include #include "featureadddialog.h" +#include "featurepresetsdialog.h" class QHBoxLayout; class QLabel; @@ -38,18 +39,26 @@ public: void resetAvailableFeatures() { m_featureAddDialog.resetFeatureNames(); } void addAvailableFeatures(const QStringList& featureNames) { m_featureAddDialog.addFeatureNames(featureNames); } + void setPresets(QList* presets) { m_featurePresetsDialog.setPresets(presets); } + void setFeatureUISet(FeatureUISet *featureUISet) { m_featurePresetsDialog.setFeatureUISet(featureUISet); } + void setPluginAPI(PluginAPI *pluginAPI) { m_featurePresetsDialog.setPluginAPI(pluginAPI); } + void setWebAPIAdapter(WebAPIAdapterInterface *apiAdapter) { m_featurePresetsDialog.setWebAPIAdapter(apiAdapter); } + private: QPushButton *m_addFeatureButton; + QPushButton *m_presetsButton; QWidget *m_titleBar; QHBoxLayout *m_titleBarLayout; QLabel *m_titleLabel; QPushButton *m_normalButton; QPushButton *m_closeButton; FeatureAddDialog m_featureAddDialog; + FeaturePresetsDialog m_featurePresetsDialog; private slots: void toggleFloating(); void addFeatureDialog(); + void presetsDialog(); void addFeatureEmitted(int featureIndex); signals: diff --git a/sdrgui/mainwindow.cpp b/sdrgui/mainwindow.cpp index ed2743215..541d55cd1 100644 --- a/sdrgui/mainwindow.cpp +++ b/sdrgui/mainwindow.cpp @@ -88,6 +88,9 @@ MESSAGE_CLASS_DEFINITION(MainWindow::MsgLoadPreset, Message) MESSAGE_CLASS_DEFINITION(MainWindow::MsgSavePreset, Message) MESSAGE_CLASS_DEFINITION(MainWindow::MsgDeletePreset, Message) +MESSAGE_CLASS_DEFINITION(MainWindow::MsgLoadFeatureSetPreset, Message) +MESSAGE_CLASS_DEFINITION(MainWindow::MsgSaveFeatureSetPreset, Message) +MESSAGE_CLASS_DEFINITION(MainWindow::MsgDeleteFeatureSetPreset, Message) MESSAGE_CLASS_DEFINITION(MainWindow::MsgAddDeviceSet, Message) MESSAGE_CLASS_DEFINITION(MainWindow::MsgRemoveLastDeviceSet, Message) MESSAGE_CLASS_DEFINITION(MainWindow::MsgSetDevice, Message) @@ -207,6 +210,9 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse m_pluginManager->listFeatures(featureNames); ui->featureDock->addAvailableFeatures(featureNames); addFeatureSet(); + ui->featureDock->setFeatureUISet(m_featureUIs[0]); + ui->featureDock->setPresets(m_settings.getFeatureSetPresets()); + ui->featureDock->setPluginAPI(m_pluginManager->getPluginAPI()); splash->showStatusMessage("load file input...", Qt::white); qDebug() << "MainWindow::MainWindow: select SampleSource from settings or default (file input)..."; @@ -220,6 +226,7 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse qDebug() << "MainWindow::MainWindow: load current preset settings..."; loadPresetSettings(m_settings.getWorkingPreset(), 0); + loadFeatureSetPresetSettings(m_settings.getWorkingFeatureSetPreset(), 0); splash->showStatusMessage("update preset controls...", Qt::white); qDebug() << "MainWindow::MainWindow: update preset controls..."; @@ -244,6 +251,7 @@ MainWindow::MainWindow(qtwebapp::LoggerWithFile *logger, const MainParser& parse #endif m_apiAdapter = new WebAPIAdapterGUI(*this); + ui->featureDock->setWebAPIAdapter(m_apiAdapter); m_requestMapper = new WebAPIRequestMapper(this); m_requestMapper->setAdapter(m_apiAdapter); m_apiHost = parser.getServerAddress(); @@ -650,7 +658,7 @@ void MainWindow::loadSettings() { qDebug() << "MainWindow::loadSettings"; - m_settings.load(m_pluginManager); + m_settings.load(); m_settings.sortPresets(); int middleIndex = m_settings.getPresetCount() / 2; QTreeWidgetItem *treeItem; @@ -742,6 +750,33 @@ void MainWindow::savePresetSettings(Preset* preset, int tabIndex) preset->setLayout(saveState()); } +void MainWindow::loadFeatureSetPresetSettings(const FeatureSetPreset* preset, int featureSetIndex) +{ + qDebug("MainWindow::loadFeatureSetPresetSettings: preset [%s | %s]", + qPrintable(preset->getGroup()), + qPrintable(preset->getDescription())); + + if (featureSetIndex >= 0) + { + FeatureUISet *featureSetUI = m_featureUIs[featureSetIndex]; + featureSetUI->loadFeatureSetSettings(preset, m_pluginManager->getPluginAPI(), m_apiAdapter); + } +} + +void MainWindow::saveFeatureSetPresetSettings(FeatureSetPreset* preset, int featureSetIndex) +{ + qDebug("MainWindow::saveFeatureSetPresetSettings: preset [%s | %s]", + qPrintable(preset->getGroup()), + qPrintable(preset->getDescription())); + + // Save from currently selected source tab + //int currentSourceTabIndex = ui->tabInputsView->currentIndex(); + FeatureUISet *featureUI = m_featureUIs[featureSetIndex]; + + preset->clearFeatures(); + featureUI->saveFeatureSetSettings(preset); +} + void MainWindow::saveCommandSettings() { } @@ -767,7 +802,8 @@ void MainWindow::closeEvent(QCloseEvent *closeEvent) qDebug("MainWindow::closeEvent"); savePresetSettings(m_settings.getWorkingPreset(), 0); - m_settings.save(m_pluginManager); + saveFeatureSetPresetSettings(m_settings.getWorkingFeatureSetPreset(), 0); + m_settings.save(); while (m_deviceUIs.size() > 0) { @@ -871,6 +907,7 @@ QTreeWidgetItem* MainWindow::addCommandToTree(const Command* command) void MainWindow::applySettings() { loadPresetSettings(m_settings.getWorkingPreset(), 0); + loadFeatureSetPresetSettings(m_settings.getWorkingFeatureSetPreset(), 0); m_settings.sortPresets(); int middleIndex = m_settings.getPresetCount() / 2; @@ -910,7 +947,21 @@ bool MainWindow::handleMessage(const Message& cmd) savePresetSettings(notif.getPreset(), notif.getDeviceSetIndex()); if (notif.isNewPreset()) { ui->presetTree->setCurrentItem(addPresetToTree(notif.getPreset())); } m_settings.sortPresets(); - m_settings.save(m_pluginManager); + m_settings.save(); + return true; + } + else if (MsgLoadFeatureSetPreset::match(cmd)) + { + MsgLoadFeatureSetPreset& notif = (MsgLoadFeatureSetPreset&) cmd; + loadFeatureSetPresetSettings(notif.getPreset(), notif.getFeatureSetIndex()); + return true; + } + else if (MsgSaveFeatureSetPreset::match(cmd)) + { + MsgSaveFeatureSetPreset& notif = (MsgSaveFeatureSetPreset&) cmd; + saveFeatureSetPresetSettings(notif.getPreset(), notif.getFeatureSetIndex()); + m_settings.sortFeatureSetPresets(); + m_settings.save(); return true; } else if (MsgDeletePreset::match(cmd)) @@ -1252,7 +1303,7 @@ void MainWindow::on_commandOutput_clicked() void MainWindow::on_commandsSave_clicked() { saveCommandSettings(); - m_settings.save(m_pluginManager); + m_settings.save(); } void MainWindow::commandKeysConnect(QObject *object, const char *slot) @@ -1509,7 +1560,8 @@ void MainWindow::on_presetImport_clicked() void MainWindow::on_settingsSave_clicked() { savePresetSettings(m_settings.getWorkingPreset(), ui->tabInputsView->currentIndex()); - m_settings.save(m_pluginManager); + saveFeatureSetPresetSettings(m_settings.getWorkingFeatureSetPreset(), ui->tabFeatures->currentIndex()); + m_settings.save(); } void MainWindow::on_presetLoad_clicked() diff --git a/sdrgui/mainwindow.h b/sdrgui/mainwindow.h index 869520074..43cc7e45e 100644 --- a/sdrgui/mainwindow.h +++ b/sdrgui/mainwindow.h @@ -52,6 +52,7 @@ class WebAPIServer; class WebAPIAdapterGUI; class Preset; class Command; +class FeatureSetPreset; class CommandKeyReceiver; namespace qtwebapp { @@ -178,6 +179,75 @@ private: { } }; + class MsgLoadFeatureSetPreset : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const FeatureSetPreset *getPreset() const { return m_preset; } + int getFeatureSetIndex() const { return m_featureSetIndex; } + + static MsgLoadFeatureSetPreset* create(const FeatureSetPreset *preset, int featureSetIndex) + { + return new MsgLoadFeatureSetPreset(preset, featureSetIndex); + } + + private: + const FeatureSetPreset *m_preset; + int m_featureSetIndex; + + MsgLoadFeatureSetPreset(const FeatureSetPreset *preset, int featureSetIndex) : + Message(), + m_preset(preset), + m_featureSetIndex(featureSetIndex) + { } + }; + + class MsgSaveFeatureSetPreset : public Message { + MESSAGE_CLASS_DECLARATION + + public: + FeatureSetPreset *getPreset() const { return m_preset; } + int getFeatureSetIndex() const { return m_featureSetIndex; } + bool isNewPreset() const { return m_newPreset; } + + static MsgSaveFeatureSetPreset* create(FeatureSetPreset *preset, int featureSetIndex, bool newPreset) + { + return new MsgSaveFeatureSetPreset(preset, featureSetIndex, newPreset); + } + + private: + FeatureSetPreset *m_preset; + int m_featureSetIndex; + bool m_newPreset; + + MsgSaveFeatureSetPreset(FeatureSetPreset *preset, int featureSetIndex, bool newPreset) : + Message(), + m_preset(preset), + m_featureSetIndex(featureSetIndex), + m_newPreset(newPreset) + { } + }; + + class MsgDeleteFeatureSetPreset : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const FeatureSetPreset *getPreset() const { return m_preset; } + + static MsgDeleteFeatureSetPreset* create(const FeatureSetPreset *preset) + { + return new MsgDeleteFeatureSetPreset(preset); + } + + private: + const FeatureSetPreset *m_preset; + + MsgDeleteFeatureSetPreset(const FeatureSetPreset *preset) : + Message(), + m_preset(preset) + { } + }; + class MsgAddDeviceSet : public Message { MESSAGE_CLASS_DECLARATION @@ -397,6 +467,8 @@ private: void loadSettings(); void loadPresetSettings(const Preset* preset, int tabIndex); void savePresetSettings(Preset* preset, int tabIndex); + void loadFeatureSetPresetSettings(const FeatureSetPreset* preset, int featureSetIndex); + void saveFeatureSetPresetSettings(FeatureSetPreset* preset, int featureSetIndex); void saveCommandSettings(); void createStatusBar(); diff --git a/sdrgui/resources/res.qrc b/sdrgui/resources/res.qrc index eb78d8ede..d9c770161 100644 --- a/sdrgui/resources/res.qrc +++ b/sdrgui/resources/res.qrc @@ -1,5 +1,6 @@ + star.png swap.png gridpolar.png gridrect.png diff --git a/sdrgui/resources/star.png b/sdrgui/resources/star.png new file mode 100644 index 0000000000000000000000000000000000000000..5678acb525a6cb3c95c12766787c607481d2385d GIT binary patch literal 1043 zcmV+u1nm2XP)EX>4Tx04R}tkv&MmKpe$iQ>7{uL5qkuWT;LSL`5963Pq?8YK2xEOfLNpnlvOS zE{=k0!NHHks)LKOt`4q(Aou~|}?mh0_0Ya_BG^=e4&~)2O zCE{WxyCQ~O(TRTaLPSVrmN6?yN%)Shdj$A;7vov}=l&esYR+OnKqQ`FhG`S86Hjg0 z2Iqa^2rJ4e@j3CRNf#u3C_x?gjg)Ju+qY;Xllfh#8Fk#DPPEV zta9Gstd%OPaZmoja86%d;yTSCB(Q)*NYs&n3N}!JjTo&uDHc++ANTPOx_*gV3b{7G z$T5#HG{~+W{11M2Yvm`#y`*p)Xn%2>k6|FR3)HKQ^L^|%^%EfY3|#3=f4K%sf0ABp zYSAO0XB)Vyl()5h>-Yagzf3pjk9`B+?J)7X(346Q@B0L1Z-vq=M>d3WhXj&_Du{ zg6uvGT7q7Vwy(Kh57ypmt#|Le-t+F0snlU70D;%330WNgTY*{N2hf(9l-IHQYd|w_ z2)Ml2C&+=1z*C?R=&e?(9eJLxKQ(1sRhQKL>S6!dm6Y66fGPFv3jdxuxmf^%YE^Ap zG1ne*gZ~4tL+w#3>S)3GXq@7tx@UNJxUf#9wgSh1)}UV*I0)>HHZ20Zz+_GP+JOmR zXZSyXDPS7-9F%^Qsq;V+@Cld$W`K7QFsIIBSyo7pYPCw9=lg&bpesOIfR6ANQ2W$H z^?}-u*!1|35(26-3(8!g_bK2yI(%~ehA50LQ)+(_}4>NoZDS}Z!FE<|AC z28w?T=B-Q9o(Oy~Ha39k>Z>t6BT77C=$MN+HMRRECCzP%nm}E(Mim zL&8@QL-k0C*!4$nvwBvY2>*T**sI=Ae}~vzQny6Rg%p4rA;sg7^jIwy#I*&e>RWYQ z9Z8|-(YWshpbt17cU)`J@iv!3B%4Bo|M?~#2X+BZsLxITziNMJ{s4%B*rK6Tq4WR% N002ovPDHLkV1g8H*3|$2 literal 0 HcmV?d00001 diff --git a/sdrgui/webapi/webapiadaptergui.cpp b/sdrgui/webapi/webapiadaptergui.cpp index 55b8904a7..06bf495bd 100644 --- a/sdrgui/webapi/webapiadaptergui.cpp +++ b/sdrgui/webapi/webapiadaptergui.cpp @@ -145,6 +145,8 @@ int WebAPIAdapterGUI::instanceConfigGet( WebAPIAdapterBase::webapiFormatPreferences(preferences, m_mainWindow.getMainSettings().getPreferences()); SWGSDRangel::SWGPreset *workingPreset = response.getWorkingPreset(); webAPIAdapterBase.webapiFormatPreset(workingPreset, m_mainWindow.getMainSettings().getWorkingPresetConst()); + SWGSDRangel::SWGFeatureSetPreset *workingFeatureSetPreset = response.getWorkingFeatureSetPreset(); + webAPIAdapterBase.webapiFormatFeatureSetPreset(workingFeatureSetPreset, m_mainWindow.getMainSettings().getWorkingFeatureSetPresetConst()); int nbPresets = m_mainWindow.m_settings.getPresetCount(); QList *swgPresets = response.getPresets(); @@ -166,6 +168,16 @@ int WebAPIAdapterGUI::instanceConfigGet( WebAPIAdapterBase::webapiFormatCommand(swgCommands->back(), *command); } + int nbFeatureSetPresets = m_mainWindow.m_settings.getFeatureSetPresetCount(); + QList *swgFeatureSetPresets = response.getFeaturesetpresets(); + + for (int i = 0; i < nbFeatureSetPresets; i++) + { + const FeatureSetPreset *preset = m_mainWindow.m_settings.getFeatureSetPreset(i); + swgFeatureSetPresets->append(new SWGSDRangel::SWGFeatureSetPreset); + webAPIAdapterBase.webapiFormatFeatureSetPreset(swgFeatureSetPresets->back(), *preset); + } + return 200; } @@ -190,6 +202,9 @@ int WebAPIAdapterGUI::instanceConfigPutPatch( Preset *workingPreset = m_mainWindow.m_settings.getWorkingPreset(); webAPIAdapterBase.webapiUpdatePreset(force, query.getWorkingPreset(), configKeys.m_workingPresetKeys, workingPreset); + FeatureSetPreset *workingFeatureSetPreset = m_mainWindow.m_settings.getWorkingFeatureSetPreset(); + webAPIAdapterBase.webapiUpdateFeatureSetPreset(force, query.getWorkingFeatureSetPreset(), configKeys.m_workingFeatureSetPresetKeys, workingFeatureSetPreset); + QList::const_iterator presetKeysIt = configKeys.m_presetKeys.begin(); int i = 0; for (; presetKeysIt != configKeys.m_presetKeys.end(); ++presetKeysIt, i++) @@ -210,6 +225,16 @@ int WebAPIAdapterGUI::instanceConfigPutPatch( m_mainWindow.m_settings.addCommand(newCommand); } + QList::const_iterator featureSetPresetKeysIt = configKeys.m_featureSetPresetKeys.begin(); + i = 0; + for (; featureSetPresetKeysIt != configKeys.m_featureSetPresetKeys.end(); ++featureSetPresetKeysIt, i++) + { + FeatureSetPreset *newPreset = new FeatureSetPreset(); // created with default values + SWGSDRangel::SWGFeatureSetPreset *swgPreset = query.getFeaturesetpresets()->at(i); + webAPIAdapterBase.webapiUpdateFeatureSetPreset(force, swgPreset, *featureSetPresetKeysIt, newPreset); + m_mainWindow.m_settings.addFeatureSetPreset(newPreset); + } + MainWindow::MsgApplySettings *msg = MainWindow::MsgApplySettings::create(); m_mainWindow.m_inputMessageQueue.push(msg); diff --git a/sdrsrv/feature/featureset.cpp b/sdrsrv/feature/featureset.cpp index ec5452e46..499e9e70a 100644 --- a/sdrsrv/feature/featureset.cpp +++ b/sdrsrv/feature/featureset.cpp @@ -19,6 +19,9 @@ #include "plugin/pluginapi.h" #include "feature/feature.h" +#include "feature/featureutils.h" +#include "settings/featuresetpreset.h" + #include "featureset.h" FeatureSet::FeatureSet(int tabIndex) @@ -61,6 +64,16 @@ void FeatureSet::renameFeatureInstances() } } +// sort by name +bool FeatureSet::FeatureInstanceRegistration::operator<(const FeatureInstanceRegistration& other) const +{ + if (m_feature && other.m_feature) { + return m_feature->getName() < other.m_feature->getName(); + } else { + return false; + } +} + void FeatureSet::freeFeatures() { for(int i = 0; i < m_featureInstanceRegistrations.count(); i++) @@ -100,3 +113,64 @@ Feature *FeatureSet::getFeatureAt(int featureIndex) return nullptr; } } + +void FeatureSet::loadFeatureSetSettings(const FeatureSetPreset *preset, PluginAPI *pluginAPI, WebAPIAdapterInterface *apiAdapter) +{ + qDebug("FeatureSet::loadFeatureSetSettings: Loading preset [%s | %s]", qPrintable(preset->getGroup()), qPrintable(preset->getDescription())); + + // Available feature plugins + PluginAPI::FeatureRegistrations *featureRegistrations = pluginAPI->getFeatureRegistrations(); + + // copy currently open features and clear list + FeatureInstanceRegistrations openFeatures = m_featureInstanceRegistrations; + m_featureInstanceRegistrations.clear(); + + for (int i = 0; i < openFeatures.count(); i++) + { + qDebug("FeatureSet::loadFeatureSetSettings: destroying old feature [%s]", qPrintable(openFeatures[i].m_featureName)); + openFeatures[i].m_feature->destroy(); + } + + qDebug("FeatureSet::loadFeatureSetSettings: %d feature(s) in preset", preset->getFeatureCount()); + + for (int i = 0; i < preset->getFeatureCount(); i++) + { + const FeatureSetPreset::FeatureConfig& featureConfig = preset->getFeatureConfig(i); + FeatureInstanceRegistration reg; + + // create feature instance + + for(int i = 0; i < featureRegistrations->count(); i++) + { + if (FeatureUtils::compareFeatureURIs((*featureRegistrations)[i].m_featureIdURI, featureConfig.m_featureIdURI)) + { + qDebug("FeatureSet::loadFeatureSetSettings: creating new feature [%s] from config [%s]", + qPrintable((*featureRegistrations)[i].m_featureIdURI), + qPrintable(featureConfig.m_featureIdURI)); + Feature *feature = + (*featureRegistrations)[i].m_plugin->createFeature(apiAdapter); + reg = FeatureInstanceRegistration(featureConfig.m_featureIdURI, feature); + break; + } + } + + if (reg.m_feature) + { + qDebug("FeatureSet::loadFeatureSetSettings: deserializing feature [%s]", qPrintable(featureConfig.m_featureIdURI)); + reg.m_feature->deserialize(featureConfig.m_config); + } + } + + renameFeatureInstances(); +} + +void FeatureSet::saveFeatureSetSettings(FeatureSetPreset *preset) +{ + std::sort(m_featureInstanceRegistrations.begin(), m_featureInstanceRegistrations.end()); // sort by increasing delta frequency and type + + for (int i = 0; i < m_featureInstanceRegistrations.count(); i++) + { + qDebug("FeatureSet::saveFeatureSetSettings: saving feature [%s]", qPrintable(m_featureInstanceRegistrations[i].m_featureName)); + preset->addFeature(m_featureInstanceRegistrations[i].m_featureName, m_featureInstanceRegistrations[i].m_feature->serialize()); + } +} diff --git a/sdrsrv/feature/featureset.h b/sdrsrv/feature/featureset.h index dbcac91c5..3a0c4a5c8 100644 --- a/sdrsrv/feature/featureset.h +++ b/sdrsrv/feature/featureset.h @@ -25,6 +25,7 @@ class PluginAPI; class Feature; +class FeatureSetPreset; class WebAPIAdapterInterface; class SDRGUI_API FeatureSet @@ -40,6 +41,8 @@ public: void deleteFeature(int featureIndex); const Feature *getFeatureAt(int featureIndex) const; Feature *getFeatureAt(int featureIndex); + void loadFeatureSetSettings(const FeatureSetPreset* preset, PluginAPI *pluginAPI, WebAPIAdapterInterface *apiAdapter); + void saveFeatureSetSettings(FeatureSetPreset* preset); private: struct FeatureInstanceRegistration diff --git a/sdrsrv/maincore.cpp b/sdrsrv/maincore.cpp index 6f01f645e..5bf1665ac 100644 --- a/sdrsrv/maincore.cpp +++ b/sdrsrv/maincore.cpp @@ -41,6 +41,9 @@ MESSAGE_CLASS_DEFINITION(MainCore::MsgDeleteInstance, Message) MESSAGE_CLASS_DEFINITION(MainCore::MsgLoadPreset, Message) MESSAGE_CLASS_DEFINITION(MainCore::MsgSavePreset, Message) MESSAGE_CLASS_DEFINITION(MainCore::MsgDeletePreset, Message) +MESSAGE_CLASS_DEFINITION(MainCore::MsgLoadFeatureSetPreset, Message) +MESSAGE_CLASS_DEFINITION(MainCore::MsgSaveFeatureSetPreset, Message) +MESSAGE_CLASS_DEFINITION(MainCore::MsgDeleteFeatureSetPreset, Message) MESSAGE_CLASS_DEFINITION(MainCore::MsgAddDeviceSet, Message) MESSAGE_CLASS_DEFINITION(MainCore::MsgRemoveLastDeviceSet, Message) MESSAGE_CLASS_DEFINITION(MainCore::MsgSetDevice, Message) @@ -102,7 +105,7 @@ MainCore::~MainCore() } m_apiServer->stop(); - m_settings.save(m_pluginManager); + m_settings.save(); delete m_apiServer; delete m_requestMapper; delete m_apiAdapter; @@ -136,7 +139,7 @@ bool MainCore::handleMessage(const Message& cmd) MsgSavePreset& notif = (MsgSavePreset&) cmd; savePresetSettings(notif.getPreset(), notif.getDeviceSetIndex()); m_settings.sortPresets(); - m_settings.save(m_pluginManager); + m_settings.save(); return true; } else if (MsgDeletePreset::match(cmd)) @@ -147,6 +150,28 @@ bool MainCore::handleMessage(const Message& cmd) m_settings.deletePreset(presetToDelete); return true; } + else if (MsgLoadFeatureSetPreset::match(cmd)) + { + MsgLoadFeatureSetPreset& notif = (MsgLoadFeatureSetPreset&) cmd; + loadFeatureSetPresetSettings(notif.getPreset(), notif.getFeatureSetIndex()); + return true; + } + else if (MsgSaveFeatureSetPreset::match(cmd)) + { + MsgSaveFeatureSetPreset& notif = (MsgSaveFeatureSetPreset&) cmd; + saveFeatureSetPresetSettings(notif.getPreset(), notif.getFeatureSetIndex()); + m_settings.sortPresets(); + m_settings.save(); + return true; + } + else if (MsgDeleteFeatureSetPreset::match(cmd)) + { + MsgDeleteFeatureSetPreset& notif = (MsgDeleteFeatureSetPreset&) cmd; + const FeatureSetPreset *presetToDelete = notif.getPreset(); + // remove preset from settings + m_settings.deleteFeatureSetPreset(presetToDelete); + return true; + } else if (MsgAddDeviceSet::match(cmd)) { MsgAddDeviceSet& notif = (MsgAddDeviceSet&) cmd; @@ -233,7 +258,7 @@ void MainCore::loadSettings() { qDebug() << "MainCore::loadSettings"; - m_settings.load(m_pluginManager); + m_settings.load(); m_settings.sortPresets(); setLoggingOptions(); } @@ -733,3 +758,29 @@ void MainCore::savePresetSettings(Preset* preset, int tabIndex) } } +void MainCore::loadFeatureSetPresetSettings(const FeatureSetPreset* preset, int featureSetIndex) +{ + qDebug("MainCore::loadFeatureSetPresetSettings: preset [%s | %s]", + qPrintable(preset->getGroup()), + qPrintable(preset->getDescription())); + + if (featureSetIndex >= 0) + { + FeatureSet *featureSet = m_featureSets[featureSetIndex]; + featureSet->loadFeatureSetSettings(preset, m_pluginManager->getPluginAPI(), m_apiAdapter); + } +} + +void MainCore::saveFeatureSetPresetSettings(FeatureSetPreset* preset, int featureSetIndex) +{ + qDebug("MainCore::saveFeatureSetPresetSettings: preset [%s | %s]", + qPrintable(preset->getGroup()), + qPrintable(preset->getDescription())); + + // Save from currently selected source tab + //int currentSourceTabIndex = ui->tabInputsView->currentIndex(); + FeatureSet *featureSet = m_featureSets[featureSetIndex]; + + preset->clearFeatures(); + featureSet->saveFeatureSetSettings(preset); +} diff --git a/sdrsrv/maincore.h b/sdrsrv/maincore.h index 5af3bd551..7ded180b2 100644 --- a/sdrsrv/maincore.h +++ b/sdrsrv/maincore.h @@ -149,6 +149,74 @@ private: { } }; + class MsgLoadFeatureSetPreset : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const FeatureSetPreset *getPreset() const { return m_preset; } + int getFeatureSetIndex() const { return m_featureSetIndex; } + + static MsgLoadFeatureSetPreset* create(const FeatureSetPreset *preset, int featureSetIndex) { + return new MsgLoadFeatureSetPreset(preset, featureSetIndex); + } + + private: + const FeatureSetPreset *m_preset; + int m_featureSetIndex; + + MsgLoadFeatureSetPreset(const FeatureSetPreset *preset, int featureSetIndex) : + Message(), + m_preset(preset), + m_featureSetIndex(featureSetIndex) + { } + }; + + class MsgSaveFeatureSetPreset : public Message { + MESSAGE_CLASS_DECLARATION + + public: + FeatureSetPreset *getPreset() const { return m_preset; } + int getFeatureSetIndex() const { return m_featureSetIndex; } + bool isNewPreset() const { return m_newPreset; } + + static MsgSaveFeatureSetPreset* create(FeatureSetPreset *preset, int featureSetIndex, bool newPreset) + { + return new MsgSaveFeatureSetPreset(preset, featureSetIndex, newPreset); + } + + private: + FeatureSetPreset *m_preset; + int m_featureSetIndex; + bool m_newPreset; + + MsgSaveFeatureSetPreset(FeatureSetPreset *preset, int featureSetIndex, bool newPreset) : + Message(), + m_preset(preset), + m_featureSetIndex(featureSetIndex), + m_newPreset(newPreset) + { } + }; + + class MsgDeleteFeatureSetPreset : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const FeatureSetPreset *getPreset() const { return m_preset; } + + static MsgDeleteFeatureSetPreset* create(const FeatureSetPreset *preset) + { + return new MsgDeleteFeatureSetPreset(preset); + } + + private: + const FeatureSetPreset *m_preset; + + MsgDeleteFeatureSetPreset(const FeatureSetPreset *preset) : + Message(), + m_preset(preset) + { } + }; + class MsgDeleteInstance : public Message { MESSAGE_CLASS_DECLARATION @@ -356,6 +424,8 @@ private: void applySettings(); void loadPresetSettings(const Preset* preset, int tabIndex); void savePresetSettings(Preset* preset, int tabIndex); + void loadFeatureSetPresetSettings(const FeatureSetPreset* preset, int featureSetIndex); + void saveFeatureSetPresetSettings(FeatureSetPreset* preset, int featureSetIndex); void setLoggingOptions(); bool handleMessage(const Message& cmd); diff --git a/sdrsrv/webapi/webapiadaptersrv.cpp b/sdrsrv/webapi/webapiadaptersrv.cpp index db0ada307..f4cdd3acf 100644 --- a/sdrsrv/webapi/webapiadaptersrv.cpp +++ b/sdrsrv/webapi/webapiadaptersrv.cpp @@ -144,6 +144,8 @@ int WebAPIAdapterSrv::instanceConfigGet( WebAPIAdapterBase::webapiFormatPreferences(preferences, m_mainCore.getMainSettings().getPreferences()); SWGSDRangel::SWGPreset *workingPreset = response.getWorkingPreset(); webAPIAdapterBase.webapiFormatPreset(workingPreset, m_mainCore.getMainSettings().getWorkingPresetConst()); + SWGSDRangel::SWGFeatureSetPreset *workingFeatureSetPreset = response.getWorkingFeatureSetPreset(); + webAPIAdapterBase.webapiFormatFeatureSetPreset(workingFeatureSetPreset, m_mainCore.getMainSettings().getWorkingFeatureSetPresetConst()); int nbPresets = m_mainCore.m_settings.getPresetCount(); QList *swgPresets = response.getPresets(); @@ -165,6 +167,16 @@ int WebAPIAdapterSrv::instanceConfigGet( WebAPIAdapterBase::webapiFormatCommand(swgCommands->back(), *command); } + int nbFeatureSetPresets = m_mainCore.m_settings.getFeatureSetPresetCount(); + QList *swgFeatureSetPresets = response.getFeaturesetpresets(); + + for (int i = 0; i < nbFeatureSetPresets; i++) + { + const FeatureSetPreset *preset = m_mainCore.m_settings.getFeatureSetPreset(i); + swgFeatureSetPresets->append(new SWGSDRangel::SWGFeatureSetPreset); + webAPIAdapterBase.webapiFormatFeatureSetPreset(swgFeatureSetPresets->back(), *preset); + } + return 200; } @@ -189,6 +201,9 @@ int WebAPIAdapterSrv::instanceConfigPutPatch( Preset *workingPreset = m_mainCore.m_settings.getWorkingPreset(); webAPIAdapterBase.webapiUpdatePreset(force, query.getWorkingPreset(), configKeys.m_workingPresetKeys, workingPreset); + FeatureSetPreset *workingFeatureSetPreset = m_mainCore.m_settings.getWorkingFeatureSetPreset(); + webAPIAdapterBase.webapiUpdateFeatureSetPreset(force, query.getWorkingFeatureSetPreset(), configKeys.m_workingFeatureSetPresetKeys, workingFeatureSetPreset); + QList::const_iterator presetKeysIt = configKeys.m_presetKeys.begin(); int i = 0; for (; presetKeysIt != configKeys.m_presetKeys.end(); ++presetKeysIt, i++) @@ -209,6 +224,16 @@ int WebAPIAdapterSrv::instanceConfigPutPatch( m_mainCore.m_settings.addCommand(newCommand); } + QList::const_iterator featureSetPresetKeysIt = configKeys.m_featureSetPresetKeys.begin(); + i = 0; + for (; featureSetPresetKeysIt != configKeys.m_featureSetPresetKeys.end(); ++featureSetPresetKeysIt, i++) + { + FeatureSetPreset *newPreset = new FeatureSetPreset(); // created with default values + SWGSDRangel::SWGFeatureSetPreset *swgPreset = query.getFeaturesetpresets()->at(i); + webAPIAdapterBase.webapiUpdateFeatureSetPreset(force, swgPreset, *featureSetPresetKeysIt, newPreset); + m_mainCore.m_settings.addFeatureSetPreset(newPreset); + } + MainCore::MsgApplySettings *msg = MainCore::MsgApplySettings::create(); m_mainCore.m_inputMessageQueue.push(msg);