diff --git a/tools/plugingenerator/config.json b/tools/plugingenerator/config.json
index 3ba3c4c9ca..dcc0a46306 100644
--- a/tools/plugingenerator/config.json
+++ b/tools/plugingenerator/config.json
@@ -1,4 +1,9 @@
{
+ "sdk":{
+ "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..62c79b0871 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 = []
+sdkSupport = generatorOptions["sdk"]["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 sdkSupport:
+ sdkPath = generatorOptions["sdk"]["project_path"]
+ if not sdkPath:
+ sdkPath = os.path.join(pathToScopy, "ScopySDK")
+ else:
+ sdkPath = os.path.join(sdkPath, "ScopySDK")
+ try:
+ os.mkdir(sdkPath, mode)
+ directoriesGenerated.append(sdkPath)
+ except FileExistsError:
+ print(sdkPath + " directory already exists!")
+
+ pluginsPath = os.path.join(sdkPath, "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,107 @@
else:
print(pluginSrcConfigPath + " file already exists!")
+####################################################### sdk ##################################################
+if sdkSupport:
+ sdkCmakeFuncFilePath = os.path.join(sdkPath, "SdkSupport.cmake")
+ if not os.path.exists(sdkCmakeFuncFilePath):
+ sdkCmakeFuncTemplate = Template(
+ filename="templates/sdk/sdk_cmake_func_template.mako"
+ )
+ sdkCmakeFuncContent = sdkCmakeFuncTemplate.render()
+ sdkCmakeFuncFile = open(sdkCmakeFuncFilePath, "w")
+ sdkCmakeFuncFile.write(sdkCmakeFuncContent)
+ sdkCmakeFuncFile.close()
+ filesGenerated.append(sdkCmakeFuncFilePath)
+ else:
+ print(sdkCmakeFuncFilePath + " file already exists!")
+
+ sdkCmakeFilePath = os.path.join(sdkPath, "CMakeLists.txt")
+ if not os.path.exists(sdkCmakeFilePath):
+ sdkCmakeTemplate = Template(
+ filename="templates/sdk/sdk_cmake_template.mako"
+ )
+ sdkCmakeContent = sdkCmakeTemplate.render(
+ deps_path=generatorOptions["sdk"]["deps_path"],
+ plugin_dir=pluginDirName,
+ plugin_name=pluginName
+ )
+ sdkCmakeFile = open(sdkCmakeFilePath, "w")
+ sdkCmakeFile.write(sdkCmakeContent)
+ sdkCmakeFile.close()
+ filesGenerated.append(sdkCmakeFilePath)
+ else:
+ print(sdkCmakeFilePath + " file already exists!")
+
+ sdkIncludePath = os.path.join(sdkPath, "include")
+ try:
+ os.mkdir(sdkIncludePath, mode)
+ directoriesGenerated.append(sdkIncludePath)
+ except FileExistsError:
+ print(sdkIncludePath + " directory already exists!")
+
+ sdkHeaderFilePath = os.path.join(sdkIncludePath, "sdkwindow.h")
+ if not os.path.exists(sdkHeaderFilePath):
+ sdkHeaderTemplate = Template(
+ filename="templates/sdk/sdk_header_template.mako"
+ )
+ sdkHeaderContent = sdkHeaderTemplate.render()
+ sdkHeaderFile = open(sdkHeaderFilePath, "w")
+ sdkHeaderFile.write(sdkHeaderContent)
+ sdkHeaderFile.close()
+ filesGenerated.append(sdkHeaderFilePath)
+ else:
+ print(sdkHeaderFilePath + " file already exists!")
+
+ sdkCmakeInFilePath = os.path.join(sdkIncludePath, "sdk-util_config.h.cmakein")
+ if not os.path.exists(sdkCmakeInFilePath):
+ sdkCmakeInTemplate = Template(
+ filename="templates/sdk/sdk_cmakein_template.mako"
+ )
+ sdkCmakeInTemplate = sdkCmakeInTemplate.render()
+ sdkCmakeInFile = open(sdkCmakeInFilePath, "w")
+ sdkCmakeInFile.write(sdkCmakeInTemplate)
+ sdkCmakeInFile.close()
+ filesGenerated.append(sdkCmakeInFilePath)
+ else:
+ print(sdkCmakeInFilePath + " file already exists!")
+
+ sdkSrcPath = os.path.join(sdkPath, "src")
+ try:
+ os.mkdir(sdkSrcPath, mode)
+ directoriesGenerated.append(sdkSrcPath)
+ except FileExistsError:
+ print(sdkSrcPath + " directory already exists!")
+
+ sdkSrcFilePath = os.path.join(sdkSrcPath, "main.cpp")
+ if not os.path.exists(sdkSrcFilePath):
+ sdkSrcTemplate = Template(
+ filename="templates/sdk/sdk_src_template.mako"
+ )
+ sdkSrcContent = sdkSrcTemplate.render()
+ sdkSrcFile = open(sdkSrcFilePath, "w")
+ sdkSrcFile.write(sdkSrcContent)
+ sdkSrcFile.close()
+ filesGenerated.append(sdkSrcFilePath)
+
+ sdkResPath = os.path.join(sdkPath, "res")
+ try:
+ os.mkdir(sdkResPath, mode)
+ directoriesGenerated.append(sdkResPath)
+ except FileExistsError:
+ print(sdkResPath + " directory already exists!")
+ shutil.copy(pathToScopy+"/gui/res/stylesheets/default.qss",sdkResPath)
+ sdkResQrc = os.path.join(sdkResPath, "resources.qrc")
+ if not os.path.exists(sdkResQrc):
+ resFile = open(sdkResQrc, "w")
+ resFile.write("\n")
+ resFile.write(" \n")
+ resFile.write(" default.qss\n")
+ resFile.write(" \n")
+ resFile.write("")
+ resFile.close()
+ filesGenerated.append(sdkResQrc)
+
##################################################### Include ################################################
includePath = os.path.join(newPluginPath, "include")
try:
@@ -171,8 +294,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 sdkSupport:
+ if generatorOptions["test"]["tst_pluginloader"]:
+ testCmakeFile.write("include(ScopyTest)\n\nsetup_scopy_tests(pluginloader)")
filesGenerated.append(testCmakePath)
else:
print(testCmakePath + " file already exists!")
@@ -214,11 +338,14 @@
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,
+ sdk_en=sdkSupport,
+ scopy_module=pluginName,
plugin_display_name=pluginDisplayName,
plugin_description=pluginDecription, config=generatorOptions["cmakelists"]
)
diff --git a/tools/plugingenerator/templates/cmakelists_template.mako b/tools/plugingenerator/templates/cmakelists_template.mako
index dede1117ef..a3f047b8cd 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 oot_en:
+target_include_directories(${"${PROJECT_NAME}"} PRIVATE ${"${SDK_DEPS_INCLUDE}"})
+
+include(${"${CMAKE_SOURCE_DIR}"}/SdkSupport.cmake)
+inlcude_dirs(${"${SDK_DEPS_INCLUDE}"})
+
+target_link_libraries(${"${PROJECT_NAME}"} PUBLIC Qt::Widgets Qt::Core)
+
+link_libs(${"${SDK_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/sdk/sdk_cmake_func_template.mako b/tools/plugingenerator/templates/sdk/sdk_cmake_func_template.mako
new file mode 100644
index 0000000000..1ffb4998c5
--- /dev/null
+++ b/tools/plugingenerator/templates/sdk/sdk_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/sdk/sdk_cmake_template.mako b/tools/plugingenerator/templates/sdk/sdk_cmake_template.mako
new file mode 100644
index 0000000000..c421b78cd0
--- /dev/null
+++ b/tools/plugingenerator/templates/sdk/sdk_cmake_template.mako
@@ -0,0 +1,65 @@
+cmake_minimum_required(VERSION 3.9)
+
+# Project name
+set(TARGET_NAME "ScopySDK")
+
+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(SDK_DEPS_PATH ${deps_path})
+if(NOT DEFINED SDK_DEPS_PATH)
+ message(FATAL_ERROR "SDK_DEPS_PATH is required!")
+else()
+ if(NOT EXISTS ${"${SDK_DEPS_PATH}"})
+ message(FATAL_ERROR "The path=" \"${"${SDK_DEPS_PATH}"}\" " to the dependencies doesn't exist!")
+ endif()
+endif()
+set(SDK_DEPS_INCLUDE ${"${SDK_DEPS_PATH}"}/usr/local/include)
+if(NOT EXISTS ${"${SDK_DEPS_INCLUDE}"})
+ message(FATAL_ERROR "The path=" \"${"${SDK_DEPS_INCLUDE}"}\" " to the headers doesn't exist!")
+endif()
+
+set(SDK_DEPS_LIB ${"${SDK_DEPS_PATH}"}/usr/local/lib)
+if(NOT EXISTS ${"${SDK_DEPS_LIB}"})
+ message(FATAL_ERROR "The path=" \"${"${SDK_DEPS_LIB}"}\" " to the libraries doesn't exist!")
+endif()
+
+set(PLUGIN_INSTALL_PATH ${"${CMAKE_CURRENT_BINARY_DIR}"}/plugin/${plugin_dir}/libscopy-${plugin_name}.so)
+
+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/sdk-util_config.h.cmakein ${"${CMAKE_CURRENT_SOURCE_DIR}"}/include/sdk-util_config.h @ONLY)
+
+set(PROJECT_SOURCES ${"${SRC_LIST}"} ${"${HEADER_LIST}"} ${"${CMAKE_CURRENT_SOURCE_DIR}"}/include/sdk-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}"}/SdkSupport.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 ${"${SDK_DEPS_INCLUDE}"} ${"${IIO_INCLUDE_DIRS}"})
+
+inlcude_dirs(${"${SDK_DEPS_INCLUDE}"})
+# Add any extra libs to link also.
+link_libs(${"${SDK_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/sdk/sdk_cmakein_template.mako b/tools/plugingenerator/templates/sdk/sdk_cmakein_template.mako
new file mode 100644
index 0000000000..1b8ec71831
--- /dev/null
+++ b/tools/plugingenerator/templates/sdk/sdk_cmakein_template.mako
@@ -0,0 +1,6 @@
+#ifndef SDK_UTIL_H_CMAKEIN
+#define SDK_UTIL_H_CMAKEIN
+
+#define PLUGIN_INSTALL_PATH "@PLUGIN_INSTALL_PATH@"
+
+#endif // SDK_UTIL_H_CMAKEIN
diff --git a/tools/plugingenerator/templates/sdk/sdk_header_template.mako b/tools/plugingenerator/templates/sdk/sdk_header_template.mako
new file mode 100644
index 0000000000..e6db7658d1
--- /dev/null
+++ b/tools/plugingenerator/templates/sdk/sdk_header_template.mako
@@ -0,0 +1,111 @@
+#include "plugin.h"
+#include "toolmenuentry.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+class MainWidget;
+class PluginManager;
+
+class SDKWindow : public QMainWindow
+{
+ Q_OBJECT
+public:
+ SDKWindow(QWidget *parent = nullptr);
+ ~SDKWindow();
+
+public Q_SLOTS:
+ void onConnect();
+ void onDisconnect();
+Q_SIGNALS:
+ void sigLabelTextUpdated(std::string_view);
+
+private:
+ QTabWidget *m_tabWidget;
+ QWidget *m_prefPage;
+ QList m_toolList;
+ MainWidget *m_mainWidget = nullptr;
+
+ void initMainWindow();
+ void updateLabelText();
+ void initPreferencesPage();
+ void addPluginPrefPage();
+ void removePluginPrefPage();
+ QLabel *createTabLabel(QString name);
+ QWidget *addHorizontalTab(QWidget *w, QLabel *lbl);
+};
+
+class MainWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ MainWidget(QWidget *parent);
+ ~MainWidget();
+
+ QWidget *pluginPrefPage();
+ QList getPluginTools();
+
+Q_SIGNALS:
+ void connected();
+ void disconnected();
+
+private Q_SLOTS:
+ void onConnect();
+ void onDisconnect();
+ void browseFile(QLineEdit *pluginPathEdit);
+
+private:
+ void unloadPluginDetails();
+ void loadPluginDetails();
+ void btnsActivation();
+ bool validConnection(QString uri, QString cat);
+ bool isCompatible(QString uri, QString cat);
+
+ PluginManager *m_pluginManager;
+ QLineEdit *m_uriEdit;
+ QLineEdit *m_pluginPathEdit;
+ QPushButton *m_connBtn;
+ QPushButton *m_loadBtn;
+ QPushButton *m_unloadBtn;
+ QWidget *m_pluginIcon;
+ scopy::MenuCombo *m_deviceTypeCb;
+ QScrollArea *m_scrollArea;
+};
+
+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);
+ bool pluginCategory(QString category);
+
+ scopy::Plugin *m_plugin = nullptr;
+};
+
+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/sdk/sdk_src_template.mako b/tools/plugingenerator/templates/sdk/sdk_src_template.mako
new file mode 100644
index 0000000000..08359c8fb3
--- /dev/null
+++ b/tools/plugingenerator/templates/sdk/sdk_src_template.mako
@@ -0,0 +1,494 @@
+#include "sdkwindow.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "gui/stylehelper.h"
+#include "sdk-util_config.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+
+ QCoreApplication::setOrganizationName("ADI");
+ QCoreApplication::setOrganizationDomain("analog.com");
+ QCoreApplication::setApplicationName("Scopy-SDK");
+ QSettings::setDefaultFormat(QSettings::IniFormat);
+
+ QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
+ app.setStyleSheet(Util::loadStylesheetFromFile(":/default.qss"));
+ SDKWindow test;
+ test.show();
+ int ret = app.exec();
+ if(ret == 0) {
+ qInfo() << "SDK support finished successfully!";
+ }
+ return ret;
+}
+
+SDKWindow::SDKWindow(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);
+ initMainWindow();
+ initPreferencesPage();
+
+ addHorizontalTab(m_mainWidget, createTabLabel("Main"));
+ addHorizontalTab(new QWidget(), createTabLabel("About"));
+ addHorizontalTab(m_prefPage, createTabLabel("Preferences"));
+ connect(m_mainWidget, &MainWidget::connected, this, &SDKWindow::onConnect);
+ connect(m_mainWidget, &MainWidget::disconnected, this, &SDKWindow::onDisconnect);
+}
+
+SDKWindow::~SDKWindow()
+{
+ if(m_mainWidget) {
+ delete m_mainWidget;
+ m_mainWidget = nullptr;
+ }
+}
+
+void SDKWindow::onConnect()
+{
+ addPluginPrefPage();
+ 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 SDKWindow::onDisconnect()
+{
+ removePluginPrefPage();
+ for(auto t : qAsConst(m_toolList)) {
+ int idx = m_tabWidget->indexOf(t);
+ m_tabWidget->removeTab(idx);
+ m_toolList.removeOne(t);
+ }
+}
+
+void SDKWindow::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, "sdkTable");
+
+ lay->addWidget(m_tabWidget);
+}
+
+QWidget *SDKWindow::addHorizontalTab(QWidget *w, QLabel *lbl)
+{
+ 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
+ m_tabWidget->addTab(pane, "");
+ QTabBar *tabbar = m_tabWidget->tabBar();
+ tabbar->setTabButton(tabbar->count() - 1, QTabBar::LeftSide, lbl);
+ return pane;
+}
+void SDKWindow::initPreferencesPage()
+{
+ m_prefPage = new QWidget(this);
+ QGridLayout *lay = new QGridLayout(m_prefPage);
+ lay->setSpacing(0);
+ lay->setMargin(0);
+ QLabel *title = new QLabel(m_prefPage);
+ title->setText("Plugin Preferences");
+ scopy::StyleHelper::MenuHeaderLabel(title);
+ lay->addWidget(title, 0, 0, Qt::AlignTop | Qt::AlignLeft);
+ lay->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding), 2, 0);
+}
+
+void SDKWindow::addPluginPrefPage()
+{
+ QWidget *pluginPref = m_mainWidget->pluginPrefPage();
+ if(pluginPref) {
+ QGridLayout *lay = dynamic_cast(m_prefPage->layout());
+ lay->addWidget(pluginPref, 1, 0);
+ }
+}
+
+void SDKWindow::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 *SDKWindow::createTabLabel(QString name)
+{
+ QLabel *lbl = new QLabel();
+ scopy::StyleHelper::TabWidgetLabel(lbl, "tabWidgetLabel");
+ lbl->setText(name);
+ return lbl;
+}
+
+MainWidget::MainWidget(QWidget *parent)
+ : QWidget(parent)
+{
+ setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ this->setMinimumWidth(1024);
+ QGridLayout *lay = new QGridLayout(this);
+ lay->setMargin(9);
+ lay->setSpacing(6);
+
+ // load section
+ m_loadBtn = new QPushButton("Load plugin", this);
+ scopy::StyleHelper::BlueGrayButton(m_loadBtn);
+ m_loadBtn->setFixedWidth(128);
+
+ m_pluginPathEdit = new QLineEdit(this);
+ m_pluginPathEdit->setText(PLUGIN_INSTALL_PATH);
+ m_pluginPathEdit->setPlaceholderText(PLUGIN_INSTALL_PATH);
+ connect(m_loadBtn, &QPushButton::clicked, this, [this]() { browseFile(m_pluginPathEdit); });
+
+ // 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(45);
+ m_deviceTypeCb->combo()->addItem("iio");
+ m_deviceTypeCb->combo()->addItem("test");
+
+ m_connBtn = new QPushButton("Connect", this);
+ scopy::StyleHelper::BlueGrayButton(m_connBtn);
+ m_connBtn->setCheckable(true);
+ m_connBtn->setFixedWidth(128);
+ connect(m_connBtn, &QPushButton::clicked, this, &MainWidget::onConnect);
+
+ // unload
+ m_unloadBtn = new QPushButton("Unload", this);
+ scopy::StyleHelper::BlueGrayButton(m_unloadBtn);
+ m_unloadBtn->setFixedWidth(128);
+ m_unloadBtn->setEnabled(false);
+ connect(m_unloadBtn, &QPushButton::clicked, this, &MainWidget::onDisconnect);
+
+ // 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_uriEdit, 1, 0);
+ lay->addWidget(m_deviceTypeCb, 2, 0);
+ lay->addWidget(pluginPage, 3, 0);
+
+ lay->addWidget(m_loadBtn, 0, 1);
+ lay->addWidget(m_connBtn, 1, 1);
+ lay->addWidget(m_unloadBtn, 2, 1);
+ lay->addWidget(m_pluginIcon, 3, 1, Qt::AlignCenter | Qt::AlignBottom);
+}
+
+MainWidget::~MainWidget()
+{
+ if(m_pluginManager && !m_connBtn->isEnabled()) {
+ onDisconnect();
+ }
+}
+
+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()
+{
+ QString uri = m_uriEdit->text();
+ QString cat = m_deviceTypeCb->combo()->currentText();
+ QString pluginPath = m_pluginPathEdit->text();
+ if(!validConnection(uri, cat)) {
+ m_connBtn->setChecked(false);
+ return;
+ }
+ m_pluginManager = new PluginManager(pluginPath, this);
+ if(!isCompatible(uri, cat) || !m_pluginManager->plugin()->onConnect()) {
+ m_connBtn->setChecked(false);
+ delete m_pluginManager;
+ m_pluginManager = nullptr;
+ return;
+ }
+ btnsActivation();
+ loadPluginDetails();
+ Q_EMIT connected();
+}
+
+void MainWidget::onDisconnect()
+{
+ btnsActivation();
+ unloadPluginDetails();
+ m_pluginManager->plugin()->onDisconnect();
+ m_pluginManager->plugin()->unload();
+ m_connBtn->setChecked(false);
+ delete m_pluginManager;
+ m_pluginManager = nullptr;
+ Q_EMIT disconnected();
+}
+
+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::unloadPluginDetails()
+{
+ QWidget *pluginPage = m_pluginManager->plugin()->page();
+ if(pluginPage) {
+ m_scrollArea->takeWidget();
+ }
+ QWidget *pluginIcon = m_pluginManager->plugin()->icon();
+ if(pluginIcon) {
+ m_pluginIcon->layout()->removeWidget(pluginIcon);
+ delete pluginIcon;
+ }
+}
+
+void MainWidget::loadPluginDetails()
+{
+ if(m_pluginManager->plugin()->loadPage()) {
+ m_scrollArea->setWidget(m_pluginManager->plugin()->page());
+ }
+ if(m_pluginManager->plugin()->loadIcon()) {
+ m_pluginIcon->layout()->addWidget(m_pluginManager->plugin()->icon());
+ }
+}
+
+void MainWidget::btnsActivation()
+{
+ m_unloadBtn->setEnabled(!m_unloadBtn->isEnabled());
+ m_connBtn->setEnabled(!m_connBtn->isEnabled());
+}
+
+bool MainWidget::validConnection(QString uri, QString cat)
+{
+ ConnectionStrategy *connStrategy = new ConnectionStrategy(uri, this);
+ bool validConn = false;
+ validConn = connStrategy->validConnection(cat);
+ if(!validConn) {
+ m_uriEdit->clear();
+ m_uriEdit->setText("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) {
+ m_uriEdit->clear();
+ m_uriEdit->setText("The plugin is not compatible with the device or doesn't exist!");
+ }
+ return compatible;
+}
+
+PluginManager::PluginManager(QString pluginPath, QObject *parent)
+ : QObject(parent)
+{
+ loadPlugin(pluginPath);
+}
+
+PluginManager::~PluginManager()
+{
+ if(m_plugin) {
+ delete m_plugin;
+ m_plugin = nullptr;
+ }
+}
+
+void PluginManager::initPlugin()
+{
+ if(!m_plugin)
+ return;
+ m_plugin->preload();
+ m_plugin->initMetadata();
+ m_plugin->init();
+ m_plugin->loadToolList();
+ m_plugin->loadExtraButtons();
+ m_plugin->postload();
+}
+
+bool PluginManager::pluginCategory(QString category)
+{
+ if(!m_plugin)
+ return false;
+ if(category.isEmpty()) // no category selected
+ return true;
+ if(!m_plugin->metadata().contains("category")) // plugin metadata does not have category
+ return true;
+ QJsonValue categoryVal = m_plugin->metadata().value("category");
+ if(categoryVal.isString()) // single category
+ return category == m_plugin->metadata().value("category").toString();
+ if(categoryVal.isArray()) { // list category
+ QJsonArray catArray = categoryVal.toArray();
+ for(const auto &v : catArray) {
+ if(!v.isString()) {
+ continue;
+ }
+ if(v.toString() == category) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool PluginManager::pluginCompatibility(QString param, QString category)
+{
+ bool isCategory = pluginCategory(category);
+ if(!isCategory)
+ return false;
+ if(!m_plugin->compatible(param, category)) {
+ return false;
+ }
+ m_plugin->setParam(param, category);
+ m_plugin->setEnabled(true);
+ initPlugin();
+ 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; }