From 168fba4341d6a7ad82da4d795cb513608145f570 Mon Sep 17 00:00:00 2001 From: andreidanila1 Date: Thu, 1 Aug 2024 14:19:09 +0300 Subject: [PATCH] plugingenerator: Added option to generate scopy pdk project. You can now generate an out of tree (oot) plugin support project within plugin generator. To work, the scopy pdk project requires importing the libraries and dependencies that were generated with cpack. Signed-off-by: andreidanila1 --- tools/plugingenerator/config.json | 5 + tools/plugingenerator/plugin_generator.py | 169 +++- .../templates/cmakelists_template.mako | 13 + .../pdk/pdk_cmake_func_template.mako | 25 + .../templates/pdk/pdk_cmake_template.mako | 66 ++ .../templates/pdk/pdk_cmakein_template.mako | 7 + .../templates/pdk/pdk_header_template.mako | 146 ++++ .../templates/pdk/pdk_src_template.mako | 820 ++++++++++++++++++ 8 files changed, 1240 insertions(+), 11 deletions(-) create mode 100644 tools/plugingenerator/templates/pdk/pdk_cmake_func_template.mako create mode 100644 tools/plugingenerator/templates/pdk/pdk_cmake_template.mako create mode 100644 tools/plugingenerator/templates/pdk/pdk_cmakein_template.mako create mode 100644 tools/plugingenerator/templates/pdk/pdk_header_template.mako create mode 100644 tools/plugingenerator/templates/pdk/pdk_src_template.mako diff --git a/tools/plugingenerator/config.json b/tools/plugingenerator/config.json index 3ba3c4c9ca..78f36c2d89 100644 --- a/tools/plugingenerator/config.json +++ b/tools/plugingenerator/config.json @@ -1,4 +1,9 @@ { + "pdk":{ + "enable": true, + "deps_path": "", + "project_path": "" + }, "plugin": { "dir_name": "new", "plugin_name": "newplugin", diff --git a/tools/plugingenerator/plugin_generator.py b/tools/plugingenerator/plugin_generator.py index 7e176c0d54..b1452e809c 100644 --- a/tools/plugingenerator/plugin_generator.py +++ b/tools/plugingenerator/plugin_generator.py @@ -25,11 +25,6 @@ pathToScopy = args.scopy_path else: pathToScopy = os.path.dirname(os.path.dirname(os.getcwd())) - -pluginsPath = os.path.join(pathToScopy, "plugins") -if not os.path.exists(pluginsPath): - print("Couldn't find " + pluginsPath + " path!") - exit(1) pathToConfigFile = "" if args.config_file_path: @@ -49,6 +44,7 @@ filesGenerated = [] directoriesGenerated = [] +pdkSupport = generatorOptions["pdk"]["enable"] pluginDirName = generatorOptions["plugin"]["dir_name"] pluginName = generatorOptions["plugin"]["plugin_name"] pluginDisplayName = generatorOptions["plugin"]["plugin_display_name"] @@ -57,6 +53,32 @@ pluginExportMacro = "SCOPY_" + pluginName.upper() + "_EXPORT" print("Starting file generation:") + +pluginsPath = os.path.join(pathToScopy, "plugins") +if pdkSupport: + pdkPath = generatorOptions["pdk"]["project_path"] + if not pdkPath: + pdkPath = os.path.join(pathToScopy, "ScopyPluginRunner") + else: + pdkPath = os.path.join(pdkPath, "ScopyPluginRunner") + try: + os.makedirs(pdkPath, mode) + directoriesGenerated.append(pdkPath) + except FileExistsError: + print(pdkPath + " directory already exists!") + + pluginsPath = os.path.join(pdkPath, "plugin") + try: + os.mkdir(pluginsPath, mode) + directoriesGenerated.append(pluginsPath) + except FileExistsError: + print(pluginsPath + " directory already exists!") + + +if not os.path.exists(pluginsPath): + print("Couldn't find " + pluginsPath + " path!") + exit(1) + #################################################### Plugin dir ############################################# newPluginPath = os.path.join(pluginsPath, pluginDirName) if not os.path.exists(newPluginPath): @@ -98,6 +120,127 @@ else: print(pluginSrcConfigPath + " file already exists!") +####################################################### pdk ################################################## +if pdkSupport: + scopyPdkDeps = generatorOptions["pdk"]["deps_path"] + pdkPreferencesPath = os.path.join(pdkPath, "preferences.ini") + if not os.path.exists(pdkPreferencesPath): + pdkPreferences = open(pdkPreferencesPath, "w") + pdkPreferences.close() + + pdkCmakeFuncFilePath = os.path.join(pdkPath, "PdkSupport.cmake") + if not os.path.exists(pdkCmakeFuncFilePath): + pdkCmakeFuncTemplate = Template( + filename="templates/pdk/pdk_cmake_func_template.mako" + ) + pdkCmakeFuncContent = pdkCmakeFuncTemplate.render() + pdkCmakeFuncFile = open(pdkCmakeFuncFilePath, "w") + pdkCmakeFuncFile.write(pdkCmakeFuncContent) + pdkCmakeFuncFile.close() + filesGenerated.append(pdkCmakeFuncFilePath) + else: + print(pdkCmakeFuncFilePath + " file already exists!") + + pdkCmakeFilePath = os.path.join(pdkPath, "CMakeLists.txt") + if not os.path.exists(pdkCmakeFilePath): + pdkCmakeTemplate = Template( + filename="templates/pdk/pdk_cmake_template.mako" + ) + pdkCmakeContent = pdkCmakeTemplate.render( + deps_path=scopyPdkDeps, + plugin_dir=pluginDirName, + plugin_name=pluginName, + preferences_path = pdkPreferencesPath + ) + pdkCmakeFile = open(pdkCmakeFilePath, "w") + pdkCmakeFile.write(pdkCmakeContent) + pdkCmakeFile.close() + filesGenerated.append(pdkCmakeFilePath) + else: + print(pdkCmakeFilePath + " file already exists!") + + pdkIncludePath = os.path.join(pdkPath, "include") + try: + os.mkdir(pdkIncludePath, mode) + directoriesGenerated.append(pdkIncludePath) + except FileExistsError: + print(pdkIncludePath + " directory already exists!") + + pdkHeaderFilePath = os.path.join(pdkIncludePath, "pdkwindow.h") + if not os.path.exists(pdkHeaderFilePath): + pdkHeaderTemplate = Template( + filename="templates/pdk/pdk_header_template.mako" + ) + pdkHeaderContent = pdkHeaderTemplate.render() + pdkHeaderFile = open(pdkHeaderFilePath, "w") + pdkHeaderFile.write(pdkHeaderContent) + pdkHeaderFile.close() + filesGenerated.append(pdkHeaderFilePath) + else: + print(pdkHeaderFilePath + " file already exists!") + + pdkCmakeInFilePath = os.path.join(pdkIncludePath, "pdk-util_config.h.cmakein") + if not os.path.exists(pdkCmakeInFilePath): + pdkCmakeInTemplate = Template( + filename="templates/pdk/pdk_cmakein_template.mako" + ) + pdkCmakeInTemplate = pdkCmakeInTemplate.render() + pdkCmakeInFile = open(pdkCmakeInFilePath, "w") + pdkCmakeInFile.write(pdkCmakeInTemplate) + pdkCmakeInFile.close() + filesGenerated.append(pdkCmakeInFilePath) + else: + print(pdkCmakeInFilePath + " file already exists!") + + pdkSrcPath = os.path.join(pdkPath, "src") + try: + os.mkdir(pdkSrcPath, mode) + directoriesGenerated.append(pdkSrcPath) + except FileExistsError: + print(pdkSrcPath + " directory already exists!") + + pdkSrcFilePath = os.path.join(pdkSrcPath, "main.cpp") + if not os.path.exists(pdkSrcFilePath): + pdkSrcTemplate = Template( + filename="templates/pdk/pdk_src_template.mako" + ) + pdkSrcContent = pdkSrcTemplate.render() + pdkSrcFile = open(pdkSrcFilePath, "w") + pdkSrcFile.write(pdkSrcContent) + pdkSrcFile.close() + filesGenerated.append(pdkSrcFilePath) + + pdkResPath = os.path.join(pdkPath, "res") + try: + os.mkdir(pdkResPath, mode) + directoriesGenerated.append(pdkResPath) + except FileExistsError: + print(pdkResPath + " directory already exists!") + + qssPdkPath = os.path.join(pdkResPath,"default.qss") + defaultQssPath = os.path.join(scopyPdkDeps,"usr/local/resources/stylesheets/default.qss") + if not os.path.exists(defaultQssPath): + defaultQssPath = os.path.join(pathToScopy,"gui/res/stylesheets/default.qss") + + if os.path.exists(defaultQssPath): + if not os.path.exists(qssPdkPath): + shutil.copy(defaultQssPath,pdkResPath) + else: + print(qssPdkPath + " file already exists!") + else: + print("default.qss stylesheet couldn't be found!") + pdkResQrc = os.path.join(pdkResPath, "resources.qrc") + if not os.path.exists(pdkResQrc): + resFile = open(pdkResQrc, "w") + resFile.write("\n") + resFile.write(" \n") + if os.path.exists(qssPdkPath): + resFile.write(" default.qss\n") + resFile.write(" \n") + resFile.write("") + resFile.close() + filesGenerated.append(pdkResQrc) + ##################################################### Include ################################################ includePath = os.path.join(newPluginPath, "include") try: @@ -171,8 +314,9 @@ + str(generatorOptions["test"]["cmake_min_required"]) + ")\n\n" ) - if generatorOptions["test"]["tst_pluginloader"]: - testCmakeFile.write("include(ScopyTest)\n\nsetup_scopy_tests(pluginloader)") + if not pdkSupport: + if generatorOptions["test"]["tst_pluginloader"]: + testCmakeFile.write("include(ScopyTest)\n\nsetup_scopy_tests(pluginloader)") filesGenerated.append(testCmakePath) else: print(testCmakePath + " file already exists!") @@ -214,13 +358,16 @@ print(resQrc + " file already exists!") ##################################################### Plugin CMakeLists ######################################### + if generatorOptions["plugin"]["cmakelists"]: cmakeListsPath = os.path.join(newPluginPath, "CMakeLists.txt") cmakeTemplate = Template(filename="templates/cmakelists_template.mako") + cmakeContent = cmakeTemplate.render( - scopy_module=pluginName, - plugin_display_name=pluginDisplayName, - plugin_description=pluginDecription, config=generatorOptions["cmakelists"] + pdk_en=pdkSupport, + scopy_module=pluginName, + plugin_display_name=pluginDisplayName, + plugin_description=pluginDecription, config=generatorOptions["cmakelists"] ) if not os.path.exists(cmakeListsPath): @@ -247,7 +394,7 @@ filesGenerated.append(cmakeinFilePath) else: - print(cmakeinFilePath + " file already exists!") + print(cmakeinFilePath + " file already exists!") ##################################################### Plugin ToolList ######################################### diff --git a/tools/plugingenerator/templates/cmakelists_template.mako b/tools/plugingenerator/templates/cmakelists_template.mako index dede1117ef..306fbe03a1 100644 --- a/tools/plugingenerator/templates/cmakelists_template.mako +++ b/tools/plugingenerator/templates/cmakelists_template.mako @@ -65,6 +65,17 @@ configure_file( target_include_directories(${"${PROJECT_NAME}"} INTERFACE ${"${CMAKE_CURRENT_SOURCE_DIR}"}/include) target_include_directories(${"${PROJECT_NAME}"} PRIVATE ${"${CMAKE_CURRENT_SOURCE_DIR}"}/include/${"${SCOPY_MODULE}"}) +% if pdk_en: +target_include_directories(${"${PROJECT_NAME}"} PRIVATE ${"${PDK_DEPS_INCLUDE}"}) + +include(${"${CMAKE_SOURCE_DIR}"}/PdkSupport.cmake) +inlcude_dirs(${"${PDK_DEPS_INCLUDE}"}) + +target_link_libraries(${"${PROJECT_NAME}"} PUBLIC Qt::Widgets Qt::Core) + +link_libs(${"${PDK_DEPS_LIB}"}) + +% else: target_include_directories(${"${PROJECT_NAME}"} PUBLIC scopy-pluginbase scopy-gui) target_link_libraries( @@ -76,6 +87,8 @@ target_link_libraries( scopy-iioutil ) +% endif + if(${"${CMAKE_SYSTEM_NAME}"} MATCHES "Windows") configureinstallersettings(${"${SCOPY_MODULE}"} ${"${PLUGIN_DESCRIPTION}"} FALSE) endif() diff --git a/tools/plugingenerator/templates/pdk/pdk_cmake_func_template.mako b/tools/plugingenerator/templates/pdk/pdk_cmake_func_template.mako new file mode 100644 index 0000000000..1ffb4998c5 --- /dev/null +++ b/tools/plugingenerator/templates/pdk/pdk_cmake_func_template.mako @@ -0,0 +1,25 @@ +# Define a function to include all directories recursively +function(inlcude_dirs root_dir) + # Find all files and directories recursively + file(GLOB_RECURSE all_items LIST_DIRECTORIES true ""${"${root_dir}"}/"") + # Loop through each item found + foreach(item ${"${all_items}"}) + # Check if the item is a directory + if(IS_DIRECTORY ${"${item}"}) + message(${"${item}"}) + target_include_directories(${"${PROJECT_NAME}"} PRIVATE ${"${item}"}) + endif() + endforeach() +endfunction() + +# Define a function to link all .so files from a root directory +function(link_libs root_dir) + # Find all .so files from root_dir + file(GLOB all_libs "${"${root_dir}"}/*.so") + # Loop through each library found + foreach(lib ${"${all_libs}"}) + # Link libraries + message(${"${lib}"}) + target_link_libraries(${"${PROJECT_NAME}"} PRIVATE ${"${lib}"}) + endforeach() +endfunction() \ No newline at end of file diff --git a/tools/plugingenerator/templates/pdk/pdk_cmake_template.mako b/tools/plugingenerator/templates/pdk/pdk_cmake_template.mako new file mode 100644 index 0000000000..d5feba4bd5 --- /dev/null +++ b/tools/plugingenerator/templates/pdk/pdk_cmake_template.mako @@ -0,0 +1,66 @@ +cmake_minimum_required(VERSION 3.9) + +# Project name +set(TARGET_NAME "ScopyPluginRunner") + +project(${"${TARGET_NAME}"} VERSION 0.0.1 DESCRIPTION "Project Description") + +# Make sure CMake will take care of moc for us +set(CMAKE_AUTOMOC ON) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(PDK_DEPS_PATH ${deps_path}) +if(NOT DEFINED PDK_DEPS_PATH) + message(FATAL_ERROR "PDK_DEPS_PATH is required!") +else() + if(NOT EXISTS ${"${PDK_DEPS_PATH}"}) + message(FATAL_ERROR "The path=" \"${"${PDK_DEPS_PATH}"}\" " to the dependencies doesn't exist!") + endif() +endif() +set(PDK_DEPS_INCLUDE ${"${PDK_DEPS_PATH}"}/usr/local/include) +if(NOT EXISTS ${"${PDK_DEPS_INCLUDE}"}) + message(FATAL_ERROR "The path=" \"${"${PDK_DEPS_INCLUDE}"}\" " to the headers doesn't exist!") +endif() + +set(PDK_DEPS_LIB ${"${PDK_DEPS_PATH}"}/usr/local/lib) +if(NOT EXISTS ${"${PDK_DEPS_LIB}"}) + message(FATAL_ERROR "The path=" \"${"${PDK_DEPS_LIB}"}\" " to the libraries doesn't exist!") +endif() + +set(PLUGIN_INSTALL_PATH ${"${CMAKE_CURRENT_BINARY_DIR}"}/plugin/${plugin_dir}/libscopy-${plugin_name}.so) +set(PATH_TO_INI ${preferences_path}) + +find_package(QT NAMES Qt5 REQUIRED COMPONENTS Widgets) +find_package(Qt${"${QT_VERSION_MAJOR}"} REQUIRED COMPONENTS Widgets Core) + +file(GLOB SRC_LIST src/*.cpp) +file(GLOB HEADER_LIST include/*.h include/*.hpp) + +configure_file(include/pdk-util_config.h.cmakein ${"${CMAKE_CURRENT_SOURCE_DIR}"}/include/pdk-util_config.h @ONLY) + +set(PROJECT_SOURCES ${"${SRC_LIST}"} ${"${HEADER_LIST}"} ${"${CMAKE_CURRENT_SOURCE_DIR}"}/include/pdk-util_config.h) +find_path(IIO_INCLUDE_DIRS iio.h REQUIRED) +find_library(IIO_LIBRARIES NAMES iio libiio REQUIRED) + +add_subdirectory(plugin/${plugin_dir}) + +qt_add_resources(PROJ_RES res/resources.qrc) + +add_executable(${"${TARGET_NAME}"} ${"${PROJECT_SOURCES}"} ${"${PROJ_RES}"}) + +include(${"${CMAKE_CURRENT_SOURCE_DIR}"}/PdkSupport.cmake) + +target_include_directories(${"${TARGET_NAME}"} PRIVATE ${"${Qt${QT_VERSION_MAJOR}_INCLUDE_DIRS}"}) +target_include_directories(${"${TARGET_NAME}"} PRIVATE ${"${CMAKE_SOURCE_DIR}"}/include) +target_include_directories(${"${TARGET_NAME}"} INTERFACE ${"${IIO_INCLUDE_DIRS}"}) + +target_include_directories(${"${TARGET_NAME}"} PUBLIC ${"${PDK_DEPS_INCLUDE}"} ${"${IIO_INCLUDE_DIRS}"}) + +inlcude_dirs(${"${PDK_DEPS_INCLUDE}"}) +# Add any extra libs to link also. +link_libs(${"${PDK_DEPS_LIB}"}) +target_link_libraries( + ${"${TARGET_NAME}"} PRIVATE Qt${"${QT_VERSION_MAJOR}"}::Widgets Qt${"${QT_VERSION_MAJOR}"}::Core ${"${IIO_LIBRARIES}"} +) diff --git a/tools/plugingenerator/templates/pdk/pdk_cmakein_template.mako b/tools/plugingenerator/templates/pdk/pdk_cmakein_template.mako new file mode 100644 index 0000000000..dd26174d7c --- /dev/null +++ b/tools/plugingenerator/templates/pdk/pdk_cmakein_template.mako @@ -0,0 +1,7 @@ +#ifndef PDK_UTIL_H_CMAKEIN +#define PDK_UTIL_H_CMAKEIN + +#define PLUGIN_INSTALL_PATH "@PLUGIN_INSTALL_PATH@" +#define PATH_TO_INI "@PATH_TO_INI@" + +#endif // PDK_UTIL_H_CMAKEIN diff --git a/tools/plugingenerator/templates/pdk/pdk_header_template.mako b/tools/plugingenerator/templates/pdk/pdk_header_template.mako new file mode 100644 index 0000000000..d033dd16ee --- /dev/null +++ b/tools/plugingenerator/templates/pdk/pdk_header_template.mako @@ -0,0 +1,146 @@ +#include "plugin.h" +#include "toolmenuentry.h" +#include +#include +#include +#include +#include +#include + +class MainWidget; +class PluginManager; + +class PDKWindow : public QMainWindow +{ + Q_OBJECT +public: + PDKWindow(QWidget *parent = nullptr); + ~PDKWindow(); + +public Q_SLOTS: + void onConnect(); + void onDisconnect(); + void onInit(); + void onDeinit(); + void onLoad(); + void onUnload(); +Q_SIGNALS: + void sigLabelTextUpdated(std::string_view); + +private: + QTabWidget *m_tabWidget; + QWidget *m_prefPage; + QList m_toolList; + QList m_loadedTools; + QLabel *m_aboutPage = nullptr; + MainWidget *m_mainWidget = nullptr; + + void initMainWindow(); + void updateLabelText(); + void initPreferencesPage(); + void addPluginPrefPage(); + void removePluginPrefPage(); + QLabel *createTabLabel(QString name); + QWidget *addHorizontalTab(QWidget *w, QLabel *lbl, bool tabEnabled = true); + QWidget *buildSaveSessionPreference(); + QWidget *generalPreferences(); + void initGeneralPreferences(); +}; + +class MainWidget : public QWidget +{ + Q_OBJECT +public: + MainWidget(QWidget *parent); + ~MainWidget(); + + QString pluginAbout(); + QWidget *pluginPrefPage(); + QList getPluginTools(); + +Q_SIGNALS: + void connected(); + void disconnected(); + void init(); + void deinit(); + void loaded(); + void unloaded(); + +private Q_SLOTS: + void onConnect(); + void onDisconnect(); + void deinitialize(); + void onInit(); + void onLoad(); + void onUnload(); + void browseFile(QLineEdit *pluginPathEdit); + void deletePluginManager(); + +private: + void unloadInfoPage(); + void loadInfoPage(); + void changeVisibility(QPushButton *btn1, QPushButton *btn2); + void enableBrowseInit(bool en); + void enableLoadConn(bool en); + void updateStatusLbl(QString msg); + void loadAvailCat(); + bool validConnection(QString uri, QString cat); + bool isCompatible(QString uri, QString cat); + + PluginManager *m_pluginManager; + QLabel *m_statusLbl; + QLineEdit *m_uriEdit; + QLineEdit *m_pluginPathEdit; + QPushButton *m_browseBtn; + QPushButton *m_initBtn; + QPushButton *m_loadBtn; + QPushButton *m_unloadBtn; + QPushButton *m_connBtn; + QPushButton *m_disconnBtn; + + QWidget *m_pluginIcon; + scopy::MenuCombo *m_deviceTypeCb; + QScrollArea *m_scrollArea; + QString m_currentPlugin; + enum State + { + Unloaded, + Loaded, + Connected + } m_state; +}; + +class PluginManager : public QObject +{ + Q_OBJECT +public: + PluginManager(QString pluginPath, QObject *parent = nullptr); + ~PluginManager(); + + scopy::Plugin *plugin() const; + bool pluginCompatibility(QString param, QString category); + +Q_SIGNALS: + void requestTool(QString toolId); + +private: + void initPlugin(); + void loadPlugin(QString file); + + scopy::Plugin *m_plugin; + bool m_loaded; +}; + +class ConnectionStrategy : public QObject +{ + Q_OBJECT +public: + ConnectionStrategy(QString uri, QObject *parent = nullptr); + ~ConnectionStrategy(); + bool validConnection(QString cat); + +private: + bool iioConn(); + bool testConn(); + QString m_uri; +}; diff --git a/tools/plugingenerator/templates/pdk/pdk_src_template.mako b/tools/plugingenerator/templates/pdk/pdk_src_template.mako new file mode 100644 index 0000000000..b5a570bf70 --- /dev/null +++ b/tools/plugingenerator/templates/pdk/pdk_src_template.mako @@ -0,0 +1,820 @@ +#include "pdkwindow.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gui/stylehelper.h" +#include "gui/preferenceshelper.h" +#include "pdk-util_config.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + QCoreApplication::setOrganizationName("ADI"); + QCoreApplication::setOrganizationDomain("analog.com"); + QCoreApplication::setApplicationName("Scopy-PDK"); + QSettings::setDefaultFormat(QSettings::IniFormat); + + QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + app.setStyleSheet(Util::loadStylesheetFromFile(":/default.qss")); + PDKWindow test; + test.show(); + int ret = app.exec(); + if(ret == 0) { + qInfo() << "PDK support finished successfully!"; + } + return ret; +} + +PDKWindow::PDKWindow(QWidget *parent) + : QMainWindow(parent) +{ + setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); + setMinimumSize(1280, 720); + layout()->setMargin(9); + layout()->setSpacing(6); + scopy::StyleHelper::GetInstance()->initColorMap(); + m_mainWidget = new MainWidget(this); + m_aboutPage = new QLabel(this); + m_aboutPage->setWordWrap(true); + m_aboutPage->setAlignment(Qt::AlignTop); + initMainWindow(); + initPreferencesPage(); + + addHorizontalTab(m_mainWidget, createTabLabel("Main")); + addHorizontalTab(m_aboutPage, createTabLabel("About")); + addHorizontalTab(m_prefPage, createTabLabel("Preferences")); + connect(m_mainWidget, &MainWidget::connected, this, &PDKWindow::onConnect); + connect(m_mainWidget, &MainWidget::disconnected, this, &PDKWindow::onDisconnect); + connect(m_mainWidget, &MainWidget::init, this, &PDKWindow::onInit); + connect(m_mainWidget, &MainWidget::deinit, this, &PDKWindow::onDeinit); + connect(m_mainWidget, &MainWidget::loaded, this, &PDKWindow::onLoad); + connect(m_mainWidget, &MainWidget::unloaded, this, &PDKWindow::onUnload); +} + +PDKWindow::~PDKWindow() +{ + if(m_mainWidget) { + delete m_mainWidget; + m_mainWidget = nullptr; + } +} + +void PDKWindow::onConnect() +{ + if(!m_loadedTools.isEmpty()) { + onUnload(); + } + QList tools = m_mainWidget->getPluginTools(); + for(const scopy::ToolMenuEntry *t : qAsConst(tools)) { + if(!t->tool()) + continue; + QLabel *lbl = createTabLabel(t->name()); + QWidget *tab = addHorizontalTab(t->tool(), lbl); + connect(t, &scopy::ToolMenuEntry::updateToolEntry, this, [lbl, t]() { + if(lbl->text().compare(t->name()) != 0) { + lbl->setText(t->name()); + } + }); + m_toolList.append(tab); + } +} + +void PDKWindow::onDisconnect() +{ + QList tools = m_mainWidget->getPluginTools(); + for(const scopy::ToolMenuEntry *t : qAsConst(tools)) { + t->disconnect(this); + } + for(auto t : qAsConst(m_toolList)) { + int idx = m_tabWidget->indexOf(t); + m_tabWidget->removeTab(idx); + m_toolList.removeOne(t); + } + onLoad(); +} + +void PDKWindow::onInit() +{ + addPluginPrefPage(); + m_aboutPage->clear(); + m_aboutPage->setText(m_mainWidget->pluginAbout()); +} + +void PDKWindow::onDeinit() +{ + m_aboutPage->clear(); + removePluginPrefPage(); +} + +void PDKWindow::onLoad() +{ + QList tools = m_mainWidget->getPluginTools(); + for(const scopy::ToolMenuEntry *t : qAsConst(tools)) { + if(t->name().isEmpty()) + continue; + QLabel *lbl = createTabLabel(t->name()); + QWidget *tab = addHorizontalTab(new QWidget(), lbl, false); + m_loadedTools.append(tab); + } +} + +void PDKWindow::onUnload() +{ + if(m_loadedTools.isEmpty()) + return; + for(auto t : qAsConst(m_loadedTools)) { + int idx = m_tabWidget->indexOf(t); + m_tabWidget->removeTab(idx); + m_loadedTools.removeOne(t); + } +} + +QWidget *PDKWindow::buildSaveSessionPreference() +{ + scopy::Preferences *p = scopy::Preferences::GetInstance(); + QWidget *w = new QWidget(this); + QHBoxLayout *lay = new QHBoxLayout(w); + lay->setMargin(0); + + lay->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox(p, "general_save_session", + "Save/Load Scopy session", this)); + lay->addSpacerItem(new QSpacerItem(40, 40, QSizePolicy::Expanding, QSizePolicy::Fixed)); + lay->addWidget(new QLabel("Settings files location ", this)); + QPushButton *navigateBtn = new QPushButton("Open", this); + scopy::StyleHelper::BlueButton(navigateBtn, "navigateBtn"); + navigateBtn->setMaximumWidth(80); + connect(navigateBtn, &QPushButton::clicked, this, + [=]() { QDesktopServices::openUrl(scopy::config::settingsFolderPath()); }); + lay->addWidget(navigateBtn); + return w; +} + +QWidget *PDKWindow::generalPreferences() +{ + QWidget *page = new QWidget(this); + QVBoxLayout *lay = new QVBoxLayout(page); + initGeneralPreferences(); + scopy::Preferences *p = scopy::Preferences::GetInstance(); + + lay->setMargin(0); + lay->setSpacing(10); + page->setLayout(lay); + + // General preferences + scopy::MenuSectionWidget *generalWidget = new scopy::MenuSectionWidget(page); + scopy::MenuCollapseSection *generalSection = + new scopy::MenuCollapseSection("General", scopy::MenuCollapseSection::MHCW_NONE, generalWidget); + generalWidget->contentLayout()->setSpacing(10); + generalWidget->contentLayout()->addWidget(generalSection); + generalSection->contentLayout()->setSpacing(10); + lay->addWidget(generalWidget); + lay->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding)); + + generalSection->contentLayout()->addWidget(buildSaveSessionPreference()); + generalSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox( + p, "general_save_attached", "Save/Load tool attached state", generalSection)); + generalSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox( + p, "general_doubleclick_attach", "Doubleclick to attach/detach tool", generalSection)); + generalSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox( + p, "general_doubleclick_ctrl_opens_menu", "Doubleclick control buttons to open menu", generalSection)); + generalSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox( + p, "general_use_opengl", "Enable OpenGL plotting", generalSection)); + generalSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox( + p, "general_use_animations", "Enable menu animations", generalSection)); + generalSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox( + p, "general_check_online_version", "Enable automatic online check for updates.", generalSection)); + generalSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox( + p, "general_show_status_bar", "Enable the status bar for displaying important messages.", + generalSection)); + generalSection->contentLayout()->addWidget( + scopy::PreferencesHelper::addPreferenceCheckBox(p, "show_grid", "Show Grid", generalSection)); + generalSection->contentLayout()->addWidget( + scopy::PreferencesHelper::addPreferenceCheckBox(p, "show_graticule", "Show Graticule", generalSection)); + generalSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox( + p, "iiowidgets_use_lazy_loading", "Use Lazy Loading", generalSection)); + generalSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox( + p, "plugins_use_debugger_v2", "Use Debugger V2 plugin", generalSection)); + generalSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCombo( + p, "general_theme", "Theme", {"default", "light"}, generalSection)); + + // Debug preferences + scopy::MenuSectionWidget *debugWidget = new scopy::MenuSectionWidget(page); + scopy::MenuCollapseSection *debugSection = + new scopy::MenuCollapseSection("Debug", scopy::MenuCollapseSection::MHCW_NONE, debugWidget); + debugWidget->contentLayout()->setSpacing(10); + debugWidget->contentLayout()->addWidget(debugSection); + debugSection->contentLayout()->setSpacing(10); + lay->addWidget(debugWidget); + lay->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding)); + + debugSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCheckBox( + p, "general_show_plot_fps", "Show plot FPS", debugSection)); + debugSection->contentLayout()->addWidget(scopy::PreferencesHelper::addPreferenceCombo( + p, "general_plot_target_fps", "Plot target FPS", {"15", "20", "30", "60"}, debugSection)); + + return page; +} + +void PDKWindow::initGeneralPreferences() +{ + scopy::Preferences *p = scopy::Preferences::GetInstance(); + p->setPreferencesFilename(PATH_TO_INI); + p->load(); + p->init("general_first_run", true); + p->init("general_save_session", true); + p->init("general_save_attached", true); + p->init("general_doubleclick_attach", true); +#if defined(__arm__) + p->init("general_use_opengl", false); +#else + p->init("general_use_opengl", true); +#endif + p->init("general_use_animations", true); + p->init("general_theme", "default"); + p->init("general_language", "en"); + p->init("show_grid", true); + p->init("show_graticule", false); + p->init("iiowidgets_use_lazy_loading", true); + p->init("plugins_use_debugger_v2", true); + p->init("general_plot_target_fps", "60"); + p->init("general_show_plot_fps", true); + p->init("general_use_native_dialogs", true); + p->init("general_additional_plugin_path", ""); + p->init("general_load_decoders", true); + p->init("general_doubleclick_ctrl_opens_menu", true); + p->init("general_check_online_version", false); + p->init("general_show_status_bar", true); +} + +void PDKWindow::initMainWindow() +{ + QWidget *centralWidget = new QWidget(this); + centralWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + centralWidget->setMinimumSize(1280, 720); + setCentralWidget(centralWidget); + QHBoxLayout *lay = new QHBoxLayout(centralWidget); + lay->setMargin(9); + lay->setSpacing(6); + + m_tabWidget = new QTabWidget(centralWidget); + m_tabWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + m_tabWidget->setTabPosition(QTabWidget::TabPosition::West); + scopy::StyleHelper::BackgroundPage(m_tabWidget, "pdkTable"); + + lay->addWidget(m_tabWidget); +} + +QWidget *PDKWindow::addHorizontalTab(QWidget *w, QLabel *lbl, bool tabEnabled) +{ + QWidget *pane = new QWidget(m_tabWidget); + pane->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + QHBoxLayout *lay = new QHBoxLayout(pane); + lay->setMargin(10); + pane->setLayout(lay); + + QScrollArea *scrollArea = new QScrollArea(pane); + scrollArea->setWidget(w); + scrollArea->setWidgetResizable(true); + scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + lay->addWidget(scrollArea); + + // Hackish - so we don't override paint event + int tabIdx = m_tabWidget->addTab(pane, ""); + m_tabWidget->setTabEnabled(tabIdx, tabEnabled); + + QTabBar *tabbar = m_tabWidget->tabBar(); + tabbar->setTabButton(tabbar->count() - 1, QTabBar::LeftSide, lbl); + tabbar->tabButton(tabbar->count() - 1, QTabBar::LeftSide)->setEnabled(tabEnabled); + return pane; +} +void PDKWindow::initPreferencesPage() +{ + m_prefPage = new QWidget(this); + QGridLayout *lay = new QGridLayout(m_prefPage); + lay->setSpacing(6); + lay->setMargin(0); + + scopy::MenuSectionWidget *generalWidget = new scopy::MenuSectionWidget(m_prefPage); + scopy::MenuCollapseSection *generalSection = new scopy::MenuCollapseSection( + "Scopy preferences", scopy::MenuCollapseSection::MHCW_NONE, generalWidget); + generalWidget->contentLayout()->setSpacing(10); + generalWidget->contentLayout()->addWidget(generalSection); + generalSection->contentLayout()->setSpacing(10); + generalSection->contentLayout()->addWidget(generalPreferences()); + + lay->addWidget(generalWidget, 0, 0); + lay->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding), 2, 0); +} + +void PDKWindow::addPluginPrefPage() +{ + QWidget *pluginPref = m_mainWidget->pluginPrefPage(); + if(pluginPref) { + QGridLayout *lay = dynamic_cast(m_prefPage->layout()); + if(!lay) + return; + scopy::MenuSectionWidget *pluginWidget = new scopy::MenuSectionWidget(m_prefPage); + scopy::MenuCollapseSection *pluginSection = new scopy::MenuCollapseSection( + "Plugin preferences", scopy::MenuCollapseSection::MHCW_NONE, pluginWidget); + pluginWidget->contentLayout()->setSpacing(10); + pluginWidget->contentLayout()->addWidget(pluginSection); + pluginSection->contentLayout()->setSpacing(10); + pluginSection->contentLayout()->addWidget(pluginPref); + lay->addWidget(pluginWidget, 1, 0); + } +} + +void PDKWindow::removePluginPrefPage() +{ + QGridLayout *lay = dynamic_cast(m_prefPage->layout()); + QLayoutItem *it = lay->itemAtPosition(1, 0); + if(!it) { + return; + } + QWidget *pluginPref = it->widget(); + if(pluginPref) { + m_prefPage->layout()->removeWidget(pluginPref); + delete pluginPref; + } +} + +QLabel *PDKWindow::createTabLabel(QString name) +{ + QLabel *lbl = new QLabel(); + scopy::StyleHelper::TabWidgetLabel(lbl, "tabWidgetLabel"); + lbl->setText(name); + return lbl; +} + +MainWidget::MainWidget(QWidget *parent) + : QWidget(parent) + , m_pluginManager(nullptr) +{ + m_state = State::Unloaded; + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + this->setMinimumWidth(1024); + QGridLayout *lay = new QGridLayout(this); + lay->setMargin(9); + lay->setSpacing(6); + + m_statusLbl = new QLabel(this); + scopy::StyleHelper::WarningLabel(m_statusLbl); + m_statusLbl->setWordWrap(true); + + // browse section + m_browseBtn = new QPushButton("Browse", this); + scopy::StyleHelper::BlueButton(m_browseBtn); + m_browseBtn->setFixedWidth(128); + m_browseBtn->setFixedHeight(40); + + m_pluginPathEdit = new QLineEdit(this); + m_pluginPathEdit->setText(PLUGIN_INSTALL_PATH); + m_pluginPathEdit->setPlaceholderText(PLUGIN_INSTALL_PATH); + connect(m_browseBtn, &QPushButton::clicked, this, [this]() { + browseFile(m_pluginPathEdit); + enableLoadConn(false); + }); + + // init + m_initBtn = new QPushButton("Init", this); + scopy::StyleHelper::BlueButton(m_initBtn); + m_initBtn->setFixedWidth(128); + m_initBtn->setFixedHeight(40); + connect(m_initBtn, &QPushButton::clicked, this, &MainWidget::onInit); + + // load btn + QWidget *loadBtns = new QWidget(this); + loadBtns->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + loadBtns->setLayout(new QVBoxLayout(loadBtns)); + loadBtns->layout()->setSpacing(0); + loadBtns->layout()->setMargin(0); + + m_loadBtn = new QPushButton("Load", this); + scopy::StyleHelper::BlueButton(m_loadBtn); + m_loadBtn->setFixedWidth(128); + m_loadBtn->setFixedHeight(40); + m_loadBtn->setEnabled(false); + connect(m_loadBtn, &QPushButton::clicked, this, &MainWidget::onLoad); + + m_unloadBtn = new QPushButton("Unload", this); + scopy::StyleHelper::BlueButton(m_unloadBtn); + m_unloadBtn->setFixedWidth(128); + m_unloadBtn->setFixedHeight(40); + m_unloadBtn->setVisible(false); + connect(m_unloadBtn, &QPushButton::clicked, this, &MainWidget::onUnload); + + loadBtns->layout()->addWidget(m_loadBtn); + loadBtns->layout()->addWidget(m_unloadBtn); + + // connect section + m_uriEdit = new QLineEdit(this); + m_uriEdit->setPlaceholderText("URI"); + m_uriEdit->setText("ip:127.0.0.1"); + + m_deviceTypeCb = new scopy::MenuCombo("category", this); + m_deviceTypeCb->setFixedHeight(40); + + QWidget *connBtns = new QWidget(this); + connBtns->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); + connBtns->setLayout(new QVBoxLayout(connBtns)); + connBtns->layout()->setSpacing(0); + connBtns->layout()->setMargin(0); + + m_connBtn = new QPushButton("Connect", this); + scopy::StyleHelper::BlueButton(m_connBtn); + m_connBtn->setFixedWidth(128); + m_connBtn->setFixedHeight(40); + m_connBtn->setEnabled(false); + connect(m_connBtn, &QPushButton::clicked, this, &MainWidget::onConnect); + + m_disconnBtn = new QPushButton("Disconnect", this); + scopy::StyleHelper::BlueButton(m_disconnBtn); + m_disconnBtn->setFixedWidth(128); + m_disconnBtn->setFixedHeight(40); + m_disconnBtn->setVisible(false); + connect(m_disconnBtn, &QPushButton::clicked, this, &MainWidget::onDisconnect); + + connBtns->layout()->addWidget(m_connBtn); + connBtns->layout()->addWidget(m_disconnBtn); + + // plugin info + QWidget *pluginPage = new QWidget(this); + pluginPage->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + pluginPage->setLayout(new QVBoxLayout(pluginPage)); + pluginPage->layout()->setMargin(0); + + m_scrollArea = new QScrollArea(pluginPage); + m_scrollArea->setWidgetResizable(true); + m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + pluginPage->layout()->addWidget(m_scrollArea); + + m_pluginIcon = new QWidget(this); + m_pluginIcon->setLayout(new QHBoxLayout(m_pluginIcon)); + m_pluginIcon->layout()->setMargin(0); + m_pluginIcon->setFixedSize(100, 100); + + lay->addWidget(m_pluginPathEdit, 0, 0); + lay->addWidget(m_deviceTypeCb, 1, 0); + lay->addWidget(m_uriEdit, 2, 0); + lay->addWidget(m_statusLbl, 3, 0); + lay->addWidget(pluginPage, 4, 0); + + lay->addWidget(m_browseBtn, 0, 1, Qt::AlignTop); + lay->addWidget(m_initBtn, 1, 1, Qt::AlignTop); + lay->addWidget(loadBtns, 2, 1, Qt::AlignTop); + lay->addWidget(connBtns, 3, 1, Qt::AlignTop); + lay->addWidget(m_pluginIcon, 4, 1, Qt::AlignCenter | Qt::AlignBottom); +} + +MainWidget::~MainWidget() +{ + if(m_state == State::Connected) { + onDisconnect(); + } + if(m_state == State::Loaded) { + onUnload(); + } + if(!m_currentPlugin.isEmpty()) { + deinitialize(); + } +} + +QString MainWidget::pluginAbout() +{ + if(!m_pluginManager || !m_pluginManager->plugin()) + return ""; + return m_pluginManager->plugin()->about(); +} + +QWidget *MainWidget::pluginPrefPage() +{ + QWidget *prefPage = nullptr; + m_pluginManager->plugin()->initPreferences(); + if(m_pluginManager->plugin()->loadPreferencesPage()) + prefPage = m_pluginManager->plugin()->preferencesPage(); + + return prefPage; +} + +QList MainWidget::getPluginTools() { return m_pluginManager->plugin()->toolList(); } + +void MainWidget::onConnect() +{ + if(m_state == State::Unloaded) + onLoad(); + if(m_state != State::Loaded) + return; + if(!m_pluginManager->plugin()->onConnect()) { + updateStatusLbl("Connection failed!"); + return; + } + changeVisibility(m_disconnBtn, m_connBtn); + enableBrowseInit(false); + m_unloadBtn->setEnabled(false); + m_state = State::Connected; + updateStatusLbl(""); + Q_EMIT connected(); +} + +void MainWidget::onDisconnect() +{ + m_pluginManager->plugin()->onDisconnect(); + changeVisibility(m_disconnBtn, m_connBtn); + m_unloadBtn->setEnabled(true); + m_state = State::Loaded; + Q_EMIT disconnected(); +} + +void MainWidget::changeVisibility(QPushButton *btn1, QPushButton *btn2) +{ + btn1->setVisible(!btn1->isVisible()); + btn2->setVisible(!btn2->isVisible()); +} + +void MainWidget::enableBrowseInit(bool en) +{ + m_initBtn->setEnabled(en); + m_browseBtn->setEnabled(en); +} + +void MainWidget::enableLoadConn(bool en) +{ + m_loadBtn->setEnabled(en); + m_connBtn->setEnabled(en); +} + +void MainWidget::updateStatusLbl(QString msg) +{ + m_statusLbl->clear(); + m_statusLbl->setText(msg); +} + +void MainWidget::loadAvailCat() +{ + scopy::Plugin *plugin = m_pluginManager->plugin(); + if(plugin->metadata().isEmpty()) + return; + if(!plugin->metadata().contains("category")) // plugin metadata does not have category + return; + QJsonValue categoryVal = plugin->metadata().value("category"); + if(categoryVal.isString()) // single category + m_deviceTypeCb->combo()->addItem(categoryVal.toString()); + if(categoryVal.isArray()) { // list category + QJsonArray catArray = categoryVal.toArray(); + for(const auto &v : catArray) { + if(!v.isString()) { + continue; + } + m_deviceTypeCb->combo()->addItem(v.toString()); + } + } +} + +void MainWidget::deinitialize() +{ + if(m_pluginManager) { + QWidget *pluginIcon = m_pluginManager->plugin()->icon(); + if(pluginIcon) { + m_pluginIcon->layout()->removeWidget(pluginIcon); + delete pluginIcon; + } + } + m_deviceTypeCb->combo()->clear(); + deletePluginManager(); + Q_EMIT deinit(); +} + +void MainWidget::onInit() +{ + QString pluginPath = m_pluginPathEdit->text(); + if(pluginPath.compare(m_currentPlugin) == 0 && m_pluginManager) { + updateStatusLbl("The plugin is already initialized!"); + return; + } + deinitialize(); + m_pluginManager = new PluginManager(pluginPath, this); + if(!m_pluginManager->plugin()) { + enableLoadConn(false); + deletePluginManager(); + updateStatusLbl("The plugin couldn't be accessed! Path: " + pluginPath); + return; + } + if(m_pluginManager->plugin()->loadIcon()) { + m_pluginIcon->layout()->addWidget(m_pluginManager->plugin()->icon()); + } + m_pluginManager->plugin()->initMetadata(); + loadAvailCat(); + m_currentPlugin = pluginPath; + enableLoadConn(true); + updateStatusLbl(""); + Q_EMIT init(); +} + +void MainWidget::onLoad() +{ + QString uri = m_uriEdit->text(); + QString cat = m_deviceTypeCb->combo()->currentText(); + if(!validConnection(uri, cat) || !isCompatible(uri, cat)) { + return; + } + loadInfoPage(); + enableBrowseInit(false); + changeVisibility(m_unloadBtn, m_loadBtn); + m_state = State::Loaded; + updateStatusLbl(""); + m_deviceTypeCb->setEnabled(false); + m_uriEdit->setEnabled(false); + Q_EMIT loaded(); +} + +void MainWidget::onUnload() +{ + m_pluginManager->plugin()->unload(); + unloadInfoPage(); + enableBrowseInit(true); + changeVisibility(m_unloadBtn, m_loadBtn); + m_state = State::Unloaded; + m_deviceTypeCb->setEnabled(true); + m_uriEdit->setEnabled(true); + Q_EMIT unloaded(); +} + +void MainWidget::browseFile(QLineEdit *pluginPathEdit) +{ + QString filePath = + QFileDialog::getOpenFileName(this, "Open a file", "directoryToOpen", + "All (*);;XML Files (*.xml);;Text Files (*.txt);;BIN Files (*.bin)"); + pluginPathEdit->setText(filePath); +} + +void MainWidget::deletePluginManager() +{ + if(m_pluginManager) { + delete m_pluginManager; + m_pluginManager = nullptr; + } +} + +void MainWidget::unloadInfoPage() +{ + QWidget *pluginPage = m_pluginManager->plugin()->page(); + if(pluginPage) { + m_scrollArea->takeWidget(); + } +} + +void MainWidget::loadInfoPage() +{ + if(m_pluginManager->plugin()->loadPage()) { + m_scrollArea->setWidget(m_pluginManager->plugin()->page()); + } +} + +bool MainWidget::validConnection(QString uri, QString cat) +{ + ConnectionStrategy *connStrategy = new ConnectionStrategy(uri, this); + bool validConn = false; + validConn = connStrategy->validConnection(cat); + if(!validConn) { + updateStatusLbl("Cannot connect to URI!"); + } + connStrategy->deleteLater(); + return validConn; +} + +bool MainWidget::isCompatible(QString uri, QString cat) +{ + bool compatible = false; + compatible = m_pluginManager->pluginCompatibility(uri, cat); + if(!compatible) { + updateStatusLbl("The plugin is not compatible with the device or doesn't exist!"); + } + return compatible; +} + +PluginManager::PluginManager(QString pluginPath, QObject *parent) + : QObject(parent) + , m_loaded(false) + , m_plugin(nullptr) +{ + loadPlugin(pluginPath); +} + +PluginManager::~PluginManager() +{ + if(m_plugin) { + delete m_plugin; + m_plugin = nullptr; + } +} + +void PluginManager::initPlugin() +{ + if(!m_plugin || m_loaded) + return; + m_plugin->preload(); + m_plugin->init(); + m_plugin->loadToolList(); + m_plugin->loadExtraButtons(); + m_plugin->postload(); + m_loaded = true; +} + +bool PluginManager::pluginCompatibility(QString param, QString category) +{ + initPlugin(); + if(!m_plugin->compatible(param, category)) + return false; + m_plugin->setParam(param, category); + m_plugin->setEnabled(true); + return true; +} + +scopy::Plugin *PluginManager::plugin() const { return m_plugin; } + +void PluginManager::loadPlugin(QString file) +{ + bool ret; + scopy::Plugin *original = nullptr; + scopy::Plugin *clone = nullptr; + + if(!QFile::exists(file)) + return; + + if(!QLibrary::isLibrary(file)) + return; + + QPluginLoader qp(file); + ret = qp.load(); + if(!ret) { + qWarning() << "Cannot load library " + qp.fileName() + "- err: " + qp.errorString(); + return; + } + + QObject *inst = qp.instance(); + if(!inst) { + qWarning() << "Cannot create QObject instance from loaded library"; + return; + } + + original = qobject_cast(qp.instance()); + if(!original) { + qWarning() << "Loaded library instance is not a Plugin*"; + return; + } + + clone = original->clone(this); + if(!clone) { + qWarning() << "clone method does not clone the object"; + return; + } + + QString cloneName; + cloneName = clone->name(); + + if(cloneName == "") + return; + + m_plugin = clone; +} + +ConnectionStrategy::ConnectionStrategy(QString uri, QObject *parent) + : m_uri(uri) + , QObject(parent) +{} + +ConnectionStrategy::~ConnectionStrategy() {} + +bool ConnectionStrategy::validConnection(QString cat) +{ + bool valid = false; + if(cat.compare("iio") == 0) { + valid = iioConn(); + } else { + valid = testConn(); + } + return valid; +} + +bool ConnectionStrategy::iioConn() +{ + iio_context *ctx = iio_create_context_from_uri(m_uri.toStdString().c_str()); + if(!ctx) { + return false; + } + iio_context_destroy(ctx); + return true; +} + +bool ConnectionStrategy::testConn() { return true; }