diff options
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | CMakeLists.txt | 53 | ||||
| -rw-r--r-- | ImageViewer.qml | 121 | ||||
| -rw-r--r-- | Main.qml | 307 | ||||
| -rw-r--r-- | QmlCustomPlot.cpp | 335 | ||||
| -rw-r--r-- | QmlCustomPlot.h | 72 | ||||
| -rw-r--r-- | main.cpp | 150 | ||||
| -rw-r--r-- | request.js | 29 |
8 files changed, 1069 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6f6596f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.user* +build* diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..61c0414 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 3.16) + +project(eurydice VERSION 0.1 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Qt6 6.5 REQUIRED COMPONENTS + Widgets + Quick) +# find_package(qcustomplot REQUIRED) + +qt_standard_project_setup(REQUIRES 6.5) + +qt_add_executable(appeurydice + main.cpp + QmlCustomPlot.h + QmlCustomPlot.cpp +) + +qt_add_qml_module(appeurydice + URI eurydice + VERSION 1.0 + QML_FILES + Main.qml + QML_FILES request.js + QML_FILES ImageViewer.qml +) + +# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. +# If you are developing for iOS or macOS you should consider setting an +# explicit, fixed bundle identifier manually though. +set_target_properties(appeurydice PROPERTIES +# MACOSX_BUNDLE_GUI_IDENTIFIER com.example.appeurydice + MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} + MACOSX_BUNDLE TRUE + WIN32_EXECUTABLE TRUE +) + +target_link_libraries(appeurydice + PRIVATE + Qt6::Widgets + Qt6::Quick + qcustomplot-qt6 +) +target_compile_definitions(app${PROJECT_NAME} PRIVATE QCUSTOMPLOT_USE_LIBRARY) + +include(GNUInstallDirs) +install(TARGETS appeurydice + BUNDLE DESTINATION . + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) diff --git a/ImageViewer.qml b/ImageViewer.qml new file mode 100644 index 0000000..30a28e2 --- /dev/null +++ b/ImageViewer.qml @@ -0,0 +1,121 @@ +import QtQuick +import QtQuick.Controls + +Page { + id: root + + implicitWidth: 550 + implicitHeight: 300 + + contentItem: Control { + id: container + + property real iscale: 1 + property size fitSize: { + const ss = image.sourceSize; + const ratio = ss.width / ss.height; + return ratio < width/height ? Qt.size(height * ratio, height) : Qt.size(width, width / ratio); + } + + contentItem: Flickable { + id: flickable + + clip: true + + leftMargin: Math.max(0, width - contentWidth) / 2 // Centering the content + topMargin: Math.max(0, height - contentHeight) / 2 + + contentWidth: container.fitSize.width + contentHeight: container.fitSize.height + + rebound: Transition {} + boundsBehavior: Flickable.StopAtBounds + ScrollBar.vertical: ScrollBar {} + ScrollBar.horizontal: ScrollBar {} + + + Image { + id: image + + cache: false + smooth: false + asynchronous: true + retainWhileLoading: true + + source: apiRoot + "/sensor/image" + + width: flickable.contentWidth + height: flickable.contentHeight + + function updateImage() { + if (image.source == apiRoot + "/sensor/image") + image.source = apiRoot + "/sensor/image2" + else + image.source = apiRoot + "/sensor/image" + + // console.log("\nflickImg.width: ", flickImg.width, + // "\nflickImg.height: ", flickImg.height, + // "\nimg.width: ", img.width, + // "\nimg.width: ", img.height, + // "\nimg.width: ", img.scale, + // "\nimg.imgRatio: ", img.imgRatio, + // "\n----------------------------------------" + // ); + } + + Shortcut { + sequence: "ctrl+m" + onActivated: smooth = !smooth + } + + onStatusChanged: { + // console.log(image.source) + if (status == Image.Ready) { + updateImage(); + + ++imageFpsTimer.fpsCounter; + } + } + + MouseArea { + hoverEnabled: true + anchors.fill: parent + onWheel: function(e) { + const { width, height } = container.fitSize; + const mousePos = Qt.point(mouseX, mouseY); + + // container.iscale += e.angleDelta.y / 120; + + if (e.angleDelta.y < 0) { + container.iscale /= 1.2 + } else { + container.iscale *= 1.2 + } + + flickable.resizeContent(width * container.iscale, height * container.iscale, mousePos); + flickable.returnToBounds(); + } + } + + Timer { + id: imageFpsTimer + + interval: 1000 + repeat: true + running: true + + property int fpsCounter: 0 + + onTriggered: { + fpsLabel.text = qsTr("Fps") + ": " + fpsCounter + fpsCounter = 0 + + image.updateImage() + } + } + } + } + + background: Rectangle { color: "#121314" } + } +} diff --git a/Main.qml b/Main.qml new file mode 100644 index 0000000..a5415c2 --- /dev/null +++ b/Main.qml @@ -0,0 +1,307 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Controls.Material +import QtQuick.Layouts +import QmlCustomPlot 1.0 + +import "request.js" as XHR + +ApplicationWindow { + id: mainWindowRoot + + width: 640 + height: 480 + visible: true + // visibility: Window.Maximized + visibility: ApplicationWindow.Maximized + + title: qsTr("Hello World") + + // // pistache + // // readonly property string apiRoot: "http://rpi5:8080/v1" + // // qhttpserver + readonly property string apiRoot: "http://rpi5:8081/v1" + + Shortcut { + sequence: "ctrl+q" + onActivated: mainWindowRoot.close() + } + + Material.theme: Material.Dark + Material.accent: Material.Indigo + + function requestExposureTime(type, uri) { + console.log("request url: ", apiRoot + uri); + + XHR.sendRequest("GET", apiRoot + uri, function(response) { + + if (response.status != "200") { + console.log("response status: ", response.status); + return; + } + + let isPlainText = response.contentType.length === 0 + + if (!isPlainText) { + console.log("invalid response: ", response); + return; + } + + exposureTimeSpinBox.value = parseInt(response.content) + }); + } + + function readParams() { + var url = apiRoot + "/sensor/params" + console.log("readParams:", url); + + XHR.sendRequest("GET", url, function(response) { + + if (response.status != "200") { + console.log("response status: ", response.status); + return; + } + + let isPlainText = response.contentType.length === 0 + + if (!isPlainText) { + console.log("invalid response: ", response); + return; + } + + var json = JSON.parse(response.content); + + console.log("readParams result:", json) + + if (exposureTimeSpinBox.value != json.exposureTime) { + console.log("update exposureTimeSpinBox to", json.exposureTime) + exposureTimeSpinBox.value = json.exposureTime; + } + + if (laserLevelSpinBox.value != json.laserLevel) { + console.log("update laserLevelSpinBox to", json.laserLevel) + laserLevelSpinBox.value = json.laserLevel; + } + + if (enableAutoExposureCheckbox.checked != (json.aeEnable == "true")) { + console.log("update enableAutoExposureCheckbox to", json.aeEnable) + enableAutoExposureCheckbox.checked = json.aeEnable == "true"; + } + }); + } + + Component.onCompleted: readParams() + + function writeParams() { + var url = apiRoot + "/sensor/params"; + console.log("writeParams:", url); + + var json = new Object(); + json["aeEnable"] = enableAutoExposureCheckbox.checked; + json["exposureTime"] = exposureTimeSpinBox.value; + json["laserLevel"] = laserLevelSpinBox.value; + console.log(JSON.stringify(json)); + + XHR.sendRequest("POST", url, function(response) { + + if (response.status != "200") { + console.log("response status: ", response.status); + return; + } + + let isPlainText = response.contentType.length === 0; + + if (!isPlainText) { + console.log("invalid response: ", response); + return; + } + + console.log("writeParams result:", response.content); + + // var json = JSON.parse(response.content); + }, JSON.stringify(json)); + } + + RowLayout { + id: horizontalLayout + + anchors.fill: parent + + spacing: 0 + + Pane { + id: pane + + Layout.fillHeight: true + Layout.fillWidth: true + Layout.minimumWidth: 150 + + ColumnLayout { + id: columnLayout + + anchors.fill: parent + + function color_by_value(value) { + const limit = 0.02; + + if (value >= limit) { + return "red"; + } + + return "green"; + } + + Label { + id: fpsLabel + + Layout.fillWidth: true + Layout.alignment: Qt.AlignTop + + color: "green" + } + + Label { + id: maxDiffLabel + + Layout.fillWidth: true + Layout.alignment: Qt.AlignTop + + color: columnLayout.color_by_value(max_diff) + text: "max diff: " + max_diff.toFixed(3) + } + + Label { + id: maxDiffIdxLabel + + Layout.fillWidth: true + Layout.alignment: Qt.AlignTop + + text: "max diff: " + max_diff_idx + } + + Label { + id: minDiffLabel + + Layout.fillWidth: true + Layout.alignment: Qt.AlignTop + + color: columnLayout.color_by_value(-min_diff) + text: "min diff: " + min_diff.toFixed(3) + } + + Label { + id: minDiffIdxLabel + + Layout.fillWidth: true + Layout.alignment: Qt.AlignTop + + text: "min diff: " + min_diff_idx + } + + Label { + id: avgDiffLabel + + Layout.fillWidth: true + Layout.alignment: Qt.AlignTop + + color: columnLayout.color_by_value(avg_diff) + text: "avg diff: " + avg_diff.toFixed(3) + } + + Label { + id: medianhDiffLabel + + Layout.fillWidth: true + Layout.alignment: Qt.AlignTop + + color: columnLayout.color_by_value(median_diff) + text: "med. diff: " + median_diff.toFixed(3) + } + + Label { + text: qsTr("Exposure time (us):") + } + + SpinBox { + id: exposureTimeSpinBox + + Layout.fillWidth: true + + from: 10 + to: 30000 + stepSize: 100 + editable: true + + value: 200 + onValueChanged: writeParams() + } + + SpinBox { + id: laserLevelSpinBox + + Layout.fillWidth: true + + from: 500 + to: 30000 + stepSize: 250 + editable: true + + value: 1500 + onValueChanged: writeParams() + } + + CheckBox { + id: enableAutoExposureCheckbox + + text: qsTr("Auto exposure") + + onCheckedChanged: writeParams() + } + + Item { + Layout.fillHeight: true + } + } + } + + ImageViewer { + id: image + + Layout.fillHeight: true + Layout.fillWidth: true + Layout.minimumWidth: 200 + } + + Item { + + Layout.fillHeight: true + Layout.fillWidth: true + Layout.minimumWidth: 900 + Layout.minimumHeight: 600 + + Rectangle { + color: "red" + opacity: 0.1 + z: 101 + } + + QmlCustomPlot { + id: qmlPlot + + anchors.fill: parent + + Label { + anchors { + top: parent.top + topMargin: 8 * 2 + horizontalCenter: parent.horizontalCenter + } + text: qmlPlot.fps + } + + plot: myPlot + // Component.onCompleted: initCustomPlot() + } + } + } +} diff --git a/QmlCustomPlot.cpp b/QmlCustomPlot.cpp new file mode 100644 index 0000000..48ec917 --- /dev/null +++ b/QmlCustomPlot.cpp @@ -0,0 +1,335 @@ +#include "QmlCustomPlot.h" + +#include <QDebug> + +#include <qcustomplot.h> + +QmlCustomPlot::QmlCustomPlot(QQuickItem* parent) + : QQuickPaintedItem(parent) + , m_timerId(0) +{ + setFlag(QQuickItem::ItemHasContents, true); + setAcceptedMouseButtons(Qt::AllButtons); + + connect(this, &QQuickPaintedItem::widthChanged, + this, &QmlCustomPlot::updateCustomPlotSize); + connect(this, &QQuickPaintedItem::heightChanged, + this, &QmlCustomPlot::updateCustomPlotSize); + + auto t = new QTimer(this); + t->setInterval(1000); + t->setTimerType(Qt::VeryCoarseTimer); + t->setSingleShot(false); + + connect(t, &QTimer::timeout, this, [&](){ + setFps(m_fpsCounter); + m_fpsCounter = 0; + }); + + t->start(); +} + +QmlCustomPlot::~QmlCustomPlot() +{ + delete m_plot; + m_plot = nullptr; + + if(m_timerId != 0) + { + killTimer(m_timerId); + } +} + +void QmlCustomPlot::initCustomPlot() +{ + setPlot(new QCustomPlot()); +} + +void QmlCustomPlot::setPlot(QCustomPlot *plot) +{ + // m_plot = new QCustomPlot(); + m_plot = plot; + + m_plot->setBackgroundScaled(true); + m_plot->setBackgroundScaledMode(Qt::KeepAspectRatio); + + updateCustomPlotSize(); + m_plot->xAxis->setLabel("x"); + m_plot->yAxis->setLabel("y"); + m_plot->xAxis->setRange(-50, 50); + m_plot->yAxis->setRange(0, 100); + m_plot ->setInteractions(QCP::iRangeDrag | + QCP::iRangeZoom | + QCP::iSelectPlottables | + QCP::iSelectItems | + QCP::iSelectOther); + + if(m_profileGraph == nullptr) + { + m_profileGraph = m_plot->addGraph(); + + m_profileGraph->setLineStyle(QCPGraph::lsNone); + m_profileGraph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, 2)); + m_profileGraph->setPen(QPen(QColor(Qt::red))); + } + + startTimer(1000 / 24); + + connect(m_plot, &QCustomPlot::afterReplot, this, &QmlCustomPlot::onCustomReplot); + + m_plot->replot(); +} + +void QmlCustomPlot::setPoints(const QVector<QVector2D> points) +{ + QVector<double> x; //(qsizetype(points.count())); + QVector<double> y; //(qsizetype(points.count())); + + for(const auto & p : points) + { + // break; + if(qFuzzyIsNull(p.x()) && qFuzzyIsNull(p.y())) + { + qt_noop(); + } + + x << p.x(); + y << p.y(); + + } + + m_profileGraph->setData(x, y); + + // setLines({ + // QLineF(rand() % 100 - 50, rand() % 100, rand() % 100 - 50, rand() % 100), + // QLineF(rand() % 100 - 50, rand() % 100, rand() % 100 - 50, rand() % 100) + // }); +} + +void QmlCustomPlot::setLines(const QVector<QLineF> lines) +{ + while(m_lineItems.count() > lines.count()) + { + m_plot->removeItem(m_lineItems.takeLast()); + } + + m_lineItems.reserve(lines.count()); + + const QCPLineEnding lineEnding(QCPLineEnding::esNone); + + for(int i = 0; i < lines.count(); i++) + { + // const QPen linePen(QColor(Qt::green), 4); + // const QColor color(Qt::green); + // QRgb green = QRandomGenerator::generate(); + // QRgb blue = QRandomGenerator::generate(); + const QColor color { QRgb { QRandomGenerator::global()->generate() } }; + const QPen linePen(color, 4); + QCPItemLine * l; + + if(i >= m_lineItems.count()) + { + l = new QCPItemLine(m_plot); + l->setPen(linePen); + l->setHead(lineEnding); + l->setTail(lineEnding); + + m_lineItems << l; + } + else + { + l = m_lineItems[i]; + } + + const QPointF offset(0, 2); + + const auto & lineToSet = lines.at(i); + l->start->setCoords(lineToSet.p1() + offset); + l->end->setCoords(lineToSet.p2() + offset); + } +} + +void QmlCustomPlot::setContours(const QVector<Points2D> contours) +{ + qDebug() << "contours count" << contours.count(); + + while(m_contourGraphs.count() > contours.count()) + { + m_plot->removeGraph(m_contourGraphs.takeLast()); + } + + m_contourGraphs.reserve(contours.count()); + + for(int i = 0; i < contours.count(); i++) + { + const QColor color(rand() % 0xff, rand() % 0xff, rand() % 0xff); + const QPen contourPen(color, 4); + + QCPGraph * c; + + // create new + if(i >= m_contourGraphs.count()) + { + c = m_plot->addGraph(); + + c->setLineStyle(QCPGraph::lsNone); + c->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, 2)); + c->setPen(contourPen); + + m_contourGraphs << c; + } + // use existing graph object + else + { + c = m_contourGraphs[i]; + } + + // set data + const auto & contourToAdd = contours.at(i); + + QVector<double> x; //(qsizetype(contourToAdd.count())); + QVector<double> y; //(qsizetype(contourToAdd.count())); + + for(const auto & p : contourToAdd) + { + x << p.x(); + y << p.y() + 5; + } + + c->setData(x, y); + } +} + +void QmlCustomPlot::setFps(int fps) +{ + m_fps = fps; + emit fpsChanged(); +} + +//void QmlCustomPlot::setPlot(QCustomPlot *plot) +//{ + +//} + +void QmlCustomPlot::paint(QPainter* painter) +{ + if(m_plot) + { + + QPixmap picture(boundingRect().size().toSize()); + QCPPainter qcpPainter(&picture); + + m_plot->toPainter(&qcpPainter); + + // m_bg->topLeft->setCoords(m_plot->xAxis->range().minRange, m_plot->yAxis->range().minRange); + // m_bg->bottomRight->setCoords(m_plot->xAxis->range().maxRange, m_plot->yAxis->range().maxRange); + // m_bg->topLeft->setCoords(0, 0); + // m_bg->bottomRight->setCoords(640, 480); + + painter->drawPixmap(QPoint(), picture); + + // qDebug() << m_plot->axisRect(); + } +} + +QCustomPlot *QmlCustomPlot::plot() const +{ + return m_plot; +} + +int QmlCustomPlot::fps() const +{ + return m_fps; +} + +void QmlCustomPlot::mousePressEvent(QMouseEvent* event) +{ + routeMouseEvents(event); +} + +void QmlCustomPlot::mouseReleaseEvent(QMouseEvent* event) +{ + routeMouseEvents(event); +} + +void QmlCustomPlot::mouseMoveEvent(QMouseEvent* event) +{ + routeMouseEvents(event); +} + +void QmlCustomPlot::mouseDoubleClickEvent(QMouseEvent* event) +{ + qDebug() << Q_FUNC_INFO; + routeMouseEvents(event); +} + +void QmlCustomPlot::wheelEvent(QWheelEvent *event) +{ + routeWheelEvents(event); +} + +void QmlCustomPlot::timerEvent(QTimerEvent *event) +{ + // static double t, U; + // U = ((double)rand() / RAND_MAX) * 5; + // m_plot->graph(0)->addData(t, U); + // qDebug() << Q_FUNC_INFO << QString("Adding dot t = %1, S = %2").arg(t).arg(U); + // t++; + m_plot->replot(); +} + +void QmlCustomPlot::graphClicked(QCPAbstractPlottable* plottable) +{ + qDebug() << Q_FUNC_INFO << QString("Clicked on graph '%1 ").arg(plottable->name()); +} + +void QmlCustomPlot::routeMouseEvents(QMouseEvent* event) +{ + if(m_plot) + { + QMouseEvent* newEvent = new QMouseEvent( + event->type(), + event->position(), + event->scenePosition(), + event->globalPosition(), + event->button(), + event->buttons(), + event->modifiers(), + event->source()); + QCoreApplication::postEvent(m_plot, newEvent); + } +} + +void QmlCustomPlot::routeWheelEvents(QWheelEvent* event) +{ + if(m_plot) + { + QWheelEvent* newEvent = new QWheelEvent( + event->position(), + event->globalPosition(), + event->pixelDelta(), + event->angleDelta(), + event->buttons(), + event->modifiers(), + event->phase(), + false); + QCoreApplication::postEvent(m_plot, newEvent); + } +} + +void QmlCustomPlot::updateCustomPlotSize() +{ + if(m_plot) + { + const QRect r(0, 0, int(width()), int(height())); + m_plot->setGeometry(r); + m_plot->setViewport(r); + } +} + +void QmlCustomPlot::onCustomReplot() +{ + // qDebug() << Q_FUNC_INFO; + m_fpsCounter++; + update(); +} diff --git a/QmlCustomPlot.h b/QmlCustomPlot.h new file mode 100644 index 0000000..c56038c --- /dev/null +++ b/QmlCustomPlot.h @@ -0,0 +1,72 @@ +#pragma once + +#include <QQuickPaintedItem> +#include <qcustomplot.h> + +//class QCustomPlot; +//class QCPAbstractPlottable; +//class QCPItemPixmap; +//class QCPGraph; +using Points2D = QVector<QVector2D>; + +class QmlCustomPlot : public QQuickPaintedItem +{ + Q_OBJECT + QML_ELEMENT + Q_PROPERTY(QCustomPlot* plot READ plot WRITE setPlot NOTIFY plotChanged) + Q_PROPERTY(int fps READ fps WRITE setFps NOTIFY fpsChanged) + +public: + QmlCustomPlot(QQuickItem* parent = nullptr); + ~QmlCustomPlot() Q_DECL_OVERRIDE; + + void paint(QPainter* painter) Q_DECL_OVERRIDE; + + Q_INVOKABLE void initCustomPlot(); + +public: + QCustomPlot* plot() const; + int fps() const; + +public slots: + void setPlot(QCustomPlot *plot); + void setPoints(const QVector<QVector2D> points); + void setLines(const QVector<QLineF> lines); + void setContours(const QVector<Points2D> contours); + void setFps(int fps); + +signals: + void plotChanged(); + void fpsChanged(); + +protected: + void routeMouseEvents(QMouseEvent* event); + void routeWheelEvents(QWheelEvent* event); + + void mousePressEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + void mouseDoubleClickEvent(QMouseEvent* event) override; + void wheelEvent(QWheelEvent *event) override; + + void timerEvent(QTimerEvent *event) override; + +private: + QCustomPlot* m_plot = nullptr; + int m_timerId; + QCPItemPixmap* m_bg = nullptr; + int m_fps = 0; + int m_fpsCounter = 0; + + QCPGraph * m_profileGraph = nullptr; + QVector<QCPGraph *> m_contourGraphs; + + QVector<QCPItemLine *> m_lineItems; + +private slots: + void graphClicked(QCPAbstractPlottable * plottable); + void onCustomReplot(); + void updateCustomPlotSize(); +}; + +//Q_DECLARE_METATYPE(QCustomPlot) diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..6ea0a15 --- /dev/null +++ b/main.cpp @@ -0,0 +1,150 @@ +#include <QApplication> +#include <QNetworkAccessManager> +#include <QNetworkReply> +#include <QQmlApplicationEngine> +#include <QQmlContext> +#include <QtConcurrent/QtConcurrentRun> +#include <QTimer> + +#include "QmlCustomPlot.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + QQmlApplicationEngine engine; + + qmlRegisterType<QmlCustomPlot>("QmlCustomPlot", 1, 0, "QmlCustomPlot"); + + auto plot = new QCustomPlot(); + plot->setOpenGl(true); + engine.rootContext()->setContextProperty("myPlot", plot); + engine.rootContext()->setContextProperty("max_diff", 0.0); + engine.rootContext()->setContextProperty("avg_diff", 0.0); + engine.rootContext()->setContextProperty("median_diff", 0.0); + + QVector<qreal> x_profile; + QVector<qreal> y_profile; + QVector<qreal> prev_y_profile; + + for(int x = -640; x < 640; ++x) + { + double y = double(QRandomGenerator::global()->bounded(0, 80000)) / 100.; + + x_profile << x; + y_profile << y; + } + prev_y_profile = y_profile; + + auto graph = plot->addGraph(); + graph->setPen(QPen(QBrush(Qt::red), 2)); + graph->setLineStyle(QCPGraph::lsLine); + graph->setScatterStyle(QCPScatterStyle::ssDot); + graph->setData(x_profile, y_profile); + + QNetworkRequest request(QUrl("http://rpi5:8081/v1/pixels")); + auto manager = new QNetworkAccessManager(&app); + + QTimer pixelsAutoRestartTimer(&app); + pixelsAutoRestartTimer.setInterval(1000); + pixelsAutoRestartTimer.setSingleShot(false); + QObject::connect(&pixelsAutoRestartTimer, &QTimer::timeout, + [&](){ + manager->get(request); + }); + + QObject::connect(manager, &QNetworkAccessManager::finished, + [&](QNetworkReply *reply) { + // qDebug() << "replyFinished"; + if (reply->error()) { + qDebug() << "replyFinished:" << reply->errorString(); + return; + } + + auto jsonPixels = QJsonDocument::fromJson(reply->readAll()) + .object()["pixels"].toArray(); + + // y_profile.clear(); + + for (int i = 0; i < jsonPixels.count(); ++i) { + y_profile[i] = jsonPixels[i].toDouble(); + } + + graph->setData(x_profile, y_profile); + + QVector<qreal> y_diff(y_profile.count()); + qreal max_diff { 0. }; + qreal min_diff { 1000000. }; + size_t max_diff_idx = 0; + size_t min_diff_idx = 0; + qreal avg_diff { 0. }; + + for (size_t i = 0; i < y_profile.count(); ++i) { + y_diff[i] = y_profile[i] - prev_y_profile[i]; + + if (y_diff[i] > max_diff) { + max_diff = y_diff[i]; + max_diff_idx = i; + } + + if (y_diff[i] < min_diff) { + min_diff = y_diff[i]; + min_diff_idx = i; + } + + avg_diff += fabs(y_diff[i]); + } + + avg_diff /= y_diff.size(); + + // for (size_t i = 0; i < y_profile.size(); ++i) { + // if + // } + + // // const auto max_diff = std::max_element(y_diff.begin(), y_diff.end()); + // const auto avg_diff = std::accumulate(y_diff.begin(), y_diff.end(), 0.0) / y_diff.size(); + std::sort(y_diff.begin(), y_diff.end()); + const auto median_diff = y_diff[y_diff.size() * 3 / 4]; + + engine.rootContext()->setContextProperty("max_diff", max_diff); + engine.rootContext()->setContextProperty("max_diff_idx", int(max_diff_idx)); + engine.rootContext()->setContextProperty("min_diff", min_diff); + engine.rootContext()->setContextProperty("min_diff_idx", int(min_diff_idx)); + engine.rootContext()->setContextProperty("avg_diff", avg_diff); + engine.rootContext()->setContextProperty("median_diff", median_diff); + + manager->get(request); + pixelsAutoRestartTimer.start(); + + prev_y_profile = y_profile; + }); + + manager->get(request); + pixelsAutoRestartTimer.start(); + + // auto pixelsRequestFuture = QtConcurrent::run([&](){ + // QThread::sleep(1); + // // x_profile.clear(); + // y_profile.clear(); + + // for(int x = -640; x < 640; ++x) + // { + // double y = double(QRandomGenerator::global()->bounded(0, 80000)) / 100.; + + // // x_profile << x; + // y_profile << y; + // } + + // graph->setData(x_profile, y_profile); + // }); + + QObject::connect( + &engine, + &QQmlApplicationEngine::objectCreationFailed, + &app, + []() { QCoreApplication::exit(-1); }, + Qt::QueuedConnection); + engine.loadFromModule("eurydice", "Main"); + + return app.exec(); +} diff --git a/request.js b/request.js new file mode 100644 index 0000000..f8998d3 --- /dev/null +++ b/request.js @@ -0,0 +1,29 @@ +function sendRequest(method, url, callback, data) +{ + let request = new XMLHttpRequest(); + + request.onreadystatechange = function() { + if (request.readyState === XMLHttpRequest.DONE) { + let response = { + status : request.status, + headers : request.getAllResponseHeaders(), + contentType : request.responseType, + content : request.response + }; + + callback(response); + } + } + + request.open(method, url); + + if (data) { + // TODO: understand these headers + request.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + request.setRequestHeader("Content-length", data.length); + request.setRequestHeader("Connection", "close"); + request.send(data); + } else { + request.send(); + } +} |
