From 9d27db77771db30df8bf2378391e3e26262049bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benedek=20L=C3=A1szl=C3=B3?= Date: Wed, 15 May 2024 22:52:13 +0200 Subject: [PATCH] yippie --- .gitignore | 3 +- inc/graph/plugins/http/http.h | 4 +- inc/graph/plugins/plugin.h | 4 +- inc/graph/server/gui/mainwindow.h | 9 +- inc/graph/server/plugin.h | 2 +- inc/graph/server/spec.h | 2 + src/graph/plugins/http/CMakeLists.txt | 2 + src/graph/plugins/http/http.cpp | 76 ++++-- src/graph/server/gui/mainwindow.cpp | 61 +++-- .../server/gui/specdialog/specdialog.cpp | 16 +- src/graph/server/gui/specdialog/specdialog.ui | 240 +++++++++++++----- src/graph/server/main.cpp | 2 +- src/graph/server/plugin.cpp | 6 +- test/test.py | 16 ++ 14 files changed, 333 insertions(+), 110 deletions(-) create mode 100755 test/test.py diff --git a/.gitignore b/.gitignore index d835f77..d722044 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ build -.cache \ No newline at end of file +.cache +__pycache__ \ No newline at end of file diff --git a/inc/graph/plugins/http/http.h b/inc/graph/plugins/http/http.h index cc9fa78..48debc1 100644 --- a/inc/graph/plugins/http/http.h +++ b/inc/graph/plugins/http/http.h @@ -5,6 +5,6 @@ extern "C" { extern plugin_t plugin; - bool init(); - void destroy(); + int run(); + int destroy(); } \ No newline at end of file diff --git a/inc/graph/plugins/plugin.h b/inc/graph/plugins/plugin.h index 2077b6a..7a93002 100644 --- a/inc/graph/plugins/plugin.h +++ b/inc/graph/plugins/plugin.h @@ -12,7 +12,7 @@ typedef struct { } point_t; typedef int (*run_method_t)(); -typedef void (*destroy_t)(); +typedef int (*destroy_t)(); typedef void (*update_callback_t)(unsigned int chartIndex, const char* seriesName, const point_t* points, int count); typedef struct { @@ -20,4 +20,6 @@ typedef struct { run_method_t run_method; destroy_t destroy_method; update_callback_t update_callback; + int argc; + char** argv; } plugin_t; \ No newline at end of file diff --git a/inc/graph/server/gui/mainwindow.h b/inc/graph/server/gui/mainwindow.h index 1272037..6d45e04 100644 --- a/inc/graph/server/gui/mainwindow.h +++ b/inc/graph/server/gui/mainwindow.h @@ -2,12 +2,11 @@ #include #include -#include +#include #include #include #include #include -#include #include namespace Ui { @@ -18,13 +17,14 @@ namespace Graph::GUI { class MainWindow : public QMainWindow { Q_OBJECT private: - explicit MainWindow(QWidget* parent = nullptr); + explicit MainWindow(QWidget* parent = nullptr, int argc = 0, char** argv = nullptr); public: MainWindow(MainWindow& other) = delete; void operator=(const MainWindow&) = delete; ~MainWindow(); + static MainWindow* getInstance(int argc, char** argv); static MainWindow* getInstance(); static void close(); @@ -42,5 +42,8 @@ class MainWindow : public QMainWindow { std::vector charts; QFileDialog* fileDialog; + + int argc; + char** argv; }; } // namespace Graph::GUI diff --git a/inc/graph/server/plugin.h b/inc/graph/server/plugin.h index 30479a6..d40f024 100644 --- a/inc/graph/server/plugin.h +++ b/inc/graph/server/plugin.h @@ -22,7 +22,7 @@ class Plugin : public QObject { void exited(int code); public: - Plugin(const std::string& path, update_callback_t callback); + Plugin(const std::string& path, update_callback_t callback, int argc, char** argv); ~Plugin(); const std::string& getPath() const { return this->path; } diff --git a/inc/graph/server/spec.h b/inc/graph/server/spec.h index 2ba2819..1fd0356 100644 --- a/inc/graph/server/spec.h +++ b/inc/graph/server/spec.h @@ -13,6 +13,8 @@ struct chart_spec_t { QString title; bool grid = false; bool legend = false; + bool keep_all = true; + int keep_limit; struct { axis_spec_t x, y; } axis; diff --git a/src/graph/plugins/http/CMakeLists.txt b/src/graph/plugins/http/CMakeLists.txt index ca44733..82cf00f 100644 --- a/src/graph/plugins/http/CMakeLists.txt +++ b/src/graph/plugins/http/CMakeLists.txt @@ -1,3 +1,5 @@ file(GLOB_RECURSE HTTP_SOURCES "./*.cpp") add_library(http SHARED ${HTTP_SOURCES}) +target_link_libraries(http librum.a) +# target_link_libraries(http librum.a libflags-cpp.so) set_target_properties(http PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/plugins") \ No newline at end of file diff --git a/src/graph/plugins/http/http.cpp b/src/graph/plugins/http/http.cpp index a838a9e..50eb394 100644 --- a/src/graph/plugins/http/http.cpp +++ b/src/graph/plugins/http/http.cpp @@ -1,28 +1,72 @@ #include #include -#include +#include +#include +#include +#include +// #include #include +#include -#define PI 3.1415 -#define to_rad(x) (x / 180 / 3.1415) +using namespace Rum::HTTP; +// using namespace Flags; + +Server* server; extern "C" { +plugin_t plugin = {.version = 1, .run_method = &run, .destroy_method = &destroy}; int run() { - std::cout << "init" << std::endl; - point_t points[100]; - for (int i = 0; i < 100; i++) { - points[i].x = i; - points[i].y = i; - } + // Parser parser; + // int* port = parser.add("port", "http port", false, 8080); + // int* workers = parser.add("workers", "number of worker threads", false, 10); - plugin.update_callback(0, "1st", points, 100); + // bool* help = parser.add("help", "print help", false, false); + + // if (!parser.parse(plugin.argc, plugin.argv) || *help) { + // parser.help(); + // } + + // server = new Server(*port, *workers, DEFAULT_BUFFER_SIZE); + try { + server = new Server(8080, 10, DEFAULT_BUFFER_SIZE); + server->add_path("/add", [](const Request& req, Response& resp) { + URI uri = req.copy_uri(); + + auto params = uri.get_parameters(); + + int chart = 0; + if (params.contains("chart")) + chart = std::stoi(params.at("chart")); + + std::string series = "1st"; + if (params.contains("series")) + series = params.at("series"); + + if (params.contains("x") && params.contains("y")) { + point_t point; + + point.x = std::stod(params.at("x")); + point.y = std::stod(params.at("y")); + plugin.update_callback(chart, series.c_str(), &point, 1); + } + }); + + std::cout << "starting http server" << std::endl; + server->listen(); + + return 0; + } catch (Rum::TCP::Error e) { + std::cerr << e.what() << std::endl; + delete server; + return 1; + } +} + +int destroy() { + if (server) + server->end(); + std::cout << "stopped http server" << std::endl; return 0; } - -void destroy() { - std::cout << "destory" << std::endl; -} - -plugin_t plugin = {.version = 1, .run_method = &run, .destroy_method = &destroy}; } \ No newline at end of file diff --git a/src/graph/server/gui/mainwindow.cpp b/src/graph/server/gui/mainwindow.cpp index 92fa61e..fe60348 100644 --- a/src/graph/server/gui/mainwindow.cpp +++ b/src/graph/server/gui/mainwindow.cpp @@ -4,8 +4,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -18,6 +17,13 @@ namespace Graph::GUI { MainWindow* MainWindow::instance = nullptr; +MainWindow* MainWindow::getInstance(int argc, char** argv) { + if (!instance) + instance = new MainWindow(nullptr, argc, argv); + + return instance; +} + MainWindow* MainWindow::getInstance() { if (!instance) instance = new MainWindow(); @@ -46,30 +52,38 @@ void MainWindow::addData(unsigned int chartIndex, const char* seriesName, const double minX = std::numeric_limits::max(), maxX = std::numeric_limits::min(); double minY = std::numeric_limits::max(), maxY = std::numeric_limits::min(); + chart_spec_t spec = chart->getSpec(); + for (QAbstractSeries* series : chart->getSeries()) { if (series->name() == name) { for (int i = 0; i < count; i++) { *((QLineSeries*)series) << QPointF(points[i].x, points[i].y); } - } - for (QPointF point : ((QLineSeries*)series)->points()) { - minX = MIN(minX, point.x()); - maxX = MAX(maxX, point.x()); - minY = MIN(minY, point.y()); - maxY = MAX(maxY, point.y()); + if (!spec.keep_all && ((QLineSeries*)series)->count() > spec.keep_limit) { + ((QLineSeries*)series)->removePoints(0, ((QLineSeries*)series)->count() - spec.keep_limit); + } + + for (QPointF point : ((QLineSeries*)series)->points()) { + minX = MIN(minX, point.x()); + maxX = MAX(maxX, point.x()); + minY = MIN(minY, point.y()); + maxY = MAX(maxY, point.y()); + } + + if (spec.axis.x.auto_limit) + chart->getX()->setRange(minX, maxX); + if (spec.axis.y.auto_limit) + chart->getY()->setRange(minY, maxY); + + break; } } - if (chart->getSpec().axis.x.auto_limit) - chart->getX()->setRange(minX, maxX); - if (chart->getSpec().axis.y.auto_limit) - chart->getY()->setRange(minY, maxY); - chart->getView()->update(); } -MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) { +MainWindow::MainWindow(QWidget* parent, int argc, char** argv) : QMainWindow(parent), ui(new Ui::MainWindow), argc(argc), argv(argv) { ui->setupUi(this); // plugins @@ -126,8 +140,19 @@ void MainWindow::on_action_Load_triggered() { return; for (QString file : fileDialog->selectedFiles()) { + bool loaded = false; + for (Plugin* plugin : pluginModel->getPlugins()) { + if (plugin->getPath() == file.toStdString()) { + loaded = true; + } + } + if (loaded) { + QMessageBox::critical(this, "Error", "Plugin already loaded:\n" + file, QMessageBox::StandardButton::Ignore); + continue; + } + try { - Plugin* plugin = new Plugin(file.toStdString(), &Graph::GUI::addData); + Plugin* plugin = new Plugin(file.toStdString(), &Graph::GUI::addData, argc, argv); pluginModel->add(plugin); QThread* thread = new QThread(); @@ -135,11 +160,15 @@ void MainWindow::on_action_Load_triggered() { connect(plugin, &Plugin::exited, this, [](int code) { std::cout << "plugin exited: " << code << std::endl; }); connect(thread, &QThread::started, plugin, &Plugin::run); connect(plugin, &Plugin::exited, thread, &QThread::quit); + connect(plugin, &Plugin::exited, this, [this, file](int code) { + if (code != 0) + QMessageBox::critical(this, "Error", "Plugin exited with an error!\n" + file, QMessageBox::StandardButton::Ignore); + }); connect(thread, &QThread::finished, thread, &QThread::deleteLater); thread->start(); } catch (std::runtime_error e) { std::cout << e.what() << std::endl; - QMessageBox::critical(this, "Error", "Failed to load plugin:\n" + file, QMessageBox::StandardButton::Ignore); + QMessageBox::critical(this, "Error", "Failed to load plugin!\n" + file, QMessageBox::StandardButton::Ignore); } int row = pluginModel->rowCount(); diff --git a/src/graph/server/gui/specdialog/specdialog.cpp b/src/graph/server/gui/specdialog/specdialog.cpp index 92f8ce1..fdc805b 100644 --- a/src/graph/server/gui/specdialog/specdialog.cpp +++ b/src/graph/server/gui/specdialog/specdialog.cpp @@ -13,7 +13,7 @@ SpecDialog* SpecDialog::getInstance() { } void SpecDialog::close() { - if(instance) + if (instance) delete instance; } @@ -41,12 +41,12 @@ SpecDialog::~SpecDialog() { } chart_spec_t SpecDialog::getSpec() const { - return { - .title = ui->chartTitle->text(), - .grid = (ui->grid->checkState() == Qt::Checked), - .legend = (ui->legend->checkState() == Qt::Checked), - .axis = { - .x = {.label = ui->xTitle->text(), .auto_limit = (ui->xAutoRange->checkState() == Qt::Checked), .min = ui->xMin->value(), .max = ui->xMax->value()}, - .y = {.label = ui->yTitle->text(), .auto_limit = (ui->yAutoRange->checkState() == Qt::Checked), .min = ui->yMin->value(), .max = ui->yMax->value()}}}; + return {.title = ui->chartTitle->text(), + .grid = ui->grid->isChecked(), + .legend = ui->legend->isChecked(), + .keep_all = ui->keepAll->isChecked(), + .keep_limit = ui->keep->value(), + .axis = {.x = {.label = ui->xTitle->text(), .auto_limit = ui->xAutoRange->isChecked(), .min = ui->xMin->value(), .max = ui->xMax->value()}, + .y = {.label = ui->yTitle->text(), .auto_limit = ui->yAutoRange->isChecked(), .min = ui->yMin->value(), .max = ui->yMax->value()}}}; } } // namespace Graph::GUI \ No newline at end of file diff --git a/src/graph/server/gui/specdialog/specdialog.ui b/src/graph/server/gui/specdialog/specdialog.ui index 1a1dff4..5574b93 100644 --- a/src/graph/server/gui/specdialog/specdialog.ui +++ b/src/graph/server/gui/specdialog/specdialog.ui @@ -6,8 +6,8 @@ 0 0 - 302 - 372 + 266 + 334 @@ -70,11 +70,17 @@ - + 0 0 + + false + + + false + QFrame::StyledPanel @@ -96,41 +102,84 @@ 16 + + false + X + + false + + + false + + + true + Axis Title - - - - - - Auto-Range - - - true + + false - - - minimum + + + + 0 + 0 + - - - - - - + + false + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Range + + + + + + + Auto + + + true + + + + + + + minimum + + + + + + + <html><head/><body><p>maximum</p></body></html> - + + + + @@ -144,6 +193,9 @@ 0 + + false + QFrame::StyledPanel @@ -178,29 +230,48 @@ - - - Auto-Range + + + QFrame::StyledPanel - - true + + QFrame::Raised - - - - - - + + + + + Range + + + + + + + Auto + + + true + + + + + + + <html><head/><body><p>minimum</p></body></html> - - - - - - - + + + + + + + <html><head/><body><p>maximum</p></body></html> - + + + + @@ -210,7 +281,7 @@ - + 0 0 @@ -219,24 +290,75 @@ Display - - - - Legend - - - true - - - - - - - Grid - - - true + + + + + 0 + 0 + + + + + + Legend + + + true + + + + + + + Grid + + + true + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Keep + + + + + + + Keep All + + + true + + + + + + + 2147483647 + + + 1000 + + + + + + + diff --git a/src/graph/server/main.cpp b/src/graph/server/main.cpp index 643c368..f10dde1 100644 --- a/src/graph/server/main.cpp +++ b/src/graph/server/main.cpp @@ -3,7 +3,7 @@ int main(int argc, char** argv) { QApplication app(argc, argv); - Graph::GUI::MainWindow::getInstance()->show(); + Graph::GUI::MainWindow::getInstance(argc, argv)->show(); int result = app.exec(); Graph::GUI::MainWindow::close(); return result; diff --git a/src/graph/server/plugin.cpp b/src/graph/server/plugin.cpp index 0cc0372..bf4ce7a 100644 --- a/src/graph/server/plugin.cpp +++ b/src/graph/server/plugin.cpp @@ -6,7 +6,7 @@ #include namespace Graph { -Plugin::Plugin(const std::string& path, update_callback_t callback) : path(path) { +Plugin::Plugin(const std::string& path, update_callback_t callback, int argc, char** argv) : path(path) { handle = dlopen(path.c_str(), RTLD_LAZY); if (!handle) throw std::runtime_error(dlerror()); @@ -16,6 +16,8 @@ Plugin::Plugin(const std::string& path, update_callback_t callback) : path(path) throw std::runtime_error(dlerror()); plugin->update_callback = callback; + plugin->argc = argc; + plugin->argv = argv; } void Plugin::run() { @@ -24,7 +26,7 @@ void Plugin::run() { Plugin::~Plugin() { if (plugin->destroy_method) - plugin->destroy_method(); + emit exited(plugin->destroy_method()); if (handle) dlclose((void*)handle); diff --git a/test/test.py b/test/test.py new file mode 100755 index 0000000..dbfcd1e --- /dev/null +++ b/test/test.py @@ -0,0 +1,16 @@ +#!/bin/env python3 + +import requests +from math import sin +from time import sleep + +def main(): + x = 0.0 + while True: + requests.get(f"http://localhost:8080/add?chart=0&series=R1&x={x}&y={sin(x)}") + x += 0.01 + sleep(0.01) + + +if __name__ == "__main__": + main() \ No newline at end of file