yippie
This commit is contained in:
parent
911a174006
commit
9d27db7777
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
build
|
||||
.cache
|
||||
__pycache__
|
@ -5,6 +5,6 @@
|
||||
extern "C" {
|
||||
extern plugin_t plugin;
|
||||
|
||||
bool init();
|
||||
void destroy();
|
||||
int run();
|
||||
int destroy();
|
||||
}
|
@ -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;
|
@ -2,12 +2,11 @@
|
||||
|
||||
#include <graph/server/gui/chart/chart.h>
|
||||
#include <graph/server/plugin.h>
|
||||
#include <qfiledialog.h>
|
||||
#include <QFileDialog>
|
||||
#include <QMainWindow>
|
||||
#include <QMenuBar>
|
||||
#include <QStatusBar>
|
||||
#include <QToolBar>
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
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<ChartWidget*> charts;
|
||||
|
||||
QFileDialog* fileDialog;
|
||||
|
||||
int argc;
|
||||
char** argv;
|
||||
};
|
||||
} // namespace Graph::GUI
|
||||
|
@ -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; }
|
||||
|
@ -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;
|
||||
|
@ -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")
|
@ -1,28 +1,72 @@
|
||||
#include <graph/plugins/http/http.h>
|
||||
#include <graph/plugins/plugin.h>
|
||||
#include <cmath>
|
||||
#include <rum/http/response.h>
|
||||
#include <rum/http/server.h>
|
||||
#include <rum/http/uri.h>
|
||||
#include <rum/tcp/error.h>
|
||||
// #include <flags-cpp/flags-cpp.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#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<int>("port", "http port", false, 8080);
|
||||
// int* workers = parser.add<int>("workers", "number of worker threads", false, 10);
|
||||
|
||||
// bool* help = parser.add<bool>("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<GET>("/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;
|
||||
}
|
||||
}
|
||||
|
||||
plugin.update_callback(0, "1st", points, 100);
|
||||
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};
|
||||
}
|
@ -4,8 +4,7 @@
|
||||
#include <graph/server/plugin.h>
|
||||
#include <graph/server/spec.h>
|
||||
#include <graph/server/ui_mainwindow.h>
|
||||
#include <qobject.h>
|
||||
#include <qthread.h>
|
||||
#include <qlineseries.h>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <iostream>
|
||||
@ -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,11 +52,16 @@ void MainWindow::addData(unsigned int chartIndex, const char* seriesName, const
|
||||
double minX = std::numeric_limits<double>::max(), maxX = std::numeric_limits<double>::min();
|
||||
double minY = std::numeric_limits<double>::max(), maxY = std::numeric_limits<double>::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);
|
||||
}
|
||||
|
||||
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()) {
|
||||
@ -59,17 +70,20 @@ void MainWindow::addData(unsigned int chartIndex, const char* seriesName, const
|
||||
minY = MIN(minY, point.y());
|
||||
maxY = MAX(maxY, point.y());
|
||||
}
|
||||
}
|
||||
|
||||
if (chart->getSpec().axis.x.auto_limit)
|
||||
if (spec.axis.x.auto_limit)
|
||||
chart->getX()->setRange(minX, maxX);
|
||||
if (chart->getSpec().axis.y.auto_limit)
|
||||
if (spec.axis.y.auto_limit)
|
||||
chart->getY()->setRange(minY, maxY);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
@ -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
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>302</width>
|
||||
<height>372</height>
|
||||
<width>266</width>
|
||||
<height>334</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@ -70,11 +70,17 @@
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="acceptDrops">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
@ -96,22 +102,62 @@
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>X</string>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="xTitle">
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="frame">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Axis Title</string>
|
||||
</property>
|
||||
<property name="clearButtonEnabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_3">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Range</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="xAutoRange">
|
||||
<property name="text">
|
||||
<string>Auto-Range</string>
|
||||
<string>Auto</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
@ -136,6 +182,9 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QFrame" name="frame_2">
|
||||
<property name="sizePolicy">
|
||||
@ -144,6 +193,9 @@
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
@ -177,10 +229,26 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_4">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Range</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="yAutoRange">
|
||||
<property name="text">
|
||||
<string>Auto-Range</string>
|
||||
<string>Auto</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
@ -208,9 +276,12 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="displayTab">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@ -219,6 +290,15 @@
|
||||
<string>Display</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="displayVerticalLayout">
|
||||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QWidget" name="widget_2" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="legend">
|
||||
<property name="text">
|
||||
@ -239,6 +319,48 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="frame_5">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Keep</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="keepAll">
|
||||
<property name="text">
|
||||
<string>Keep All</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="keep">
|
||||
<property name="maximum">
|
||||
<number>2147483647</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>1000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
|
@ -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;
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include <string>
|
||||
|
||||
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);
|
||||
|
16
test/test.py
Executable file
16
test/test.py
Executable file
@ -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()
|
Loading…
Reference in New Issue
Block a user