diff options
| author | Nikita Kostovsky <nikita@kostovsky.me> | 2025-11-13 12:12:07 +0100 |
|---|---|---|
| committer | Nikita Kostovsky <nikita@kostovsky.me> | 2025-11-13 12:12:07 +0100 |
| commit | 3396ea3e7cf5a0def0ea720bcb863b374fd1cd0e (patch) | |
| tree | 55082bf91ff2dabd2957f0cf11150a7d39ababc3 | |
| parent | c33006b2a8468f48cfbace39fe7c534f910fce0d (diff) | |
implement GET_pixels in http server
| -rw-r--r-- | src/camera/veyeimx287m.cpp | 12 | ||||
| -rw-r--r-- | src/camera/veyeimx287m.h | 5 | ||||
| -rw-r--r-- | src/httpservice.cpp | 110 | ||||
| -rw-r--r-- | src/httpservice.h | 27 |
4 files changed, 148 insertions, 6 deletions
diff --git a/src/camera/veyeimx287m.cpp b/src/camera/veyeimx287m.cpp index 5b745aa..16ab344 100644 --- a/src/camera/veyeimx287m.cpp +++ b/src/camera/veyeimx287m.cpp @@ -16,7 +16,9 @@ #include <QElapsedTimer> +// orpheus #include "constants.h" +#include "httpservice.h" #include "imagealgos.h" #include "mem_utils.h" #include "pixels.h" @@ -308,6 +310,9 @@ bool VeyeIMX287m::init() if (!initCam()) return false; + if (!initHttpServer()) + return false; + return true; } @@ -620,6 +625,13 @@ bool VeyeIMX287m::initCam() return true; } +bool VeyeIMX287m::initHttpServer() +{ + m_httpServer = std::make_shared<HttpServer>(this); + + return m_httpServer != nullptr; +} + void VeyeIMX287m::dequeueFrameLoop(std::stop_token stopToken) { // std::cout << "VeyeIMX287m: start stream" << std::endl; diff --git a/src/camera/veyeimx287m.h b/src/camera/veyeimx287m.h index b28ce96..5afc84b 100644 --- a/src/camera/veyeimx287m.h +++ b/src/camera/veyeimx287m.h @@ -10,6 +10,8 @@ #include "icamera.h" +class HttpServer; + class VeyeIMX287m : public ICamera { constexpr static char videoDevice[] = "/dev/video0"; @@ -51,6 +53,7 @@ private: bool openCam(); bool selectCam(int camIdx = 0); bool initCam(); + bool initHttpServer(); void dequeueFrameLoop(std::stop_token stopToken); void calcFrameLoop(std::stop_token stopToken); @@ -82,4 +85,6 @@ private: std::jthread m_streamThread; // std::jthread m_calcThreads[1]; std::jthread m_calcThreads[4]; + + std::shared_ptr<HttpServer> m_httpServer; }; diff --git a/src/httpservice.cpp b/src/httpservice.cpp index 7b89e23..14cf60d 100644 --- a/src/httpservice.cpp +++ b/src/httpservice.cpp @@ -1,16 +1,122 @@ #include "httpservice.h" +// qt +#include <QHttpServer> + +// orpheus +#include "camera/veyeimx287m.h" #include "constants.h" +#include "image.h" +#include "imagealgos.h" #include "macro.h" +// rapidjson +#include "rapidjson/document.h" +#include "rapidjson/stringbuffer.h" +#include "rapidjson/writer.h" + extern uint8_t pgm_image[64 + img_width * img_height * sizeof(uint8_t)]; extern size_t pgm_image_size; extern std::mutex pgm_image_mtx; -HttpServer::HttpServer(const std::shared_ptr<ICamera> &camera, +HttpServer::HttpServer(ICamera *camera, + // QObject *parent, const QHostAddress &address, const uint16_t port) + // : QObject{parent} : INIT_FIELD(camera) , INIT_FIELD(address) , INIT_FIELD(port) -{} + , m_server{std::make_shared<QHttpServer>()} +{ + const auto apiPrefix = QStringLiteral("/v1"); + const auto pixelsPath = apiPrefix + "/pixels"; + qDebug().noquote() << Q_FUNC_INFO << ": pixelsPath: " << pixelsPath; + m_server->route(pixelsPath, [this]() { return GET_pixels(); }); + + qDebug().noquote() << Q_FUNC_INFO << ": listen: " << m_server->listen(m_address, m_port); +} + +QHttpServerResponse HttpServer::GET_pixels() +{ + QElapsedTimer t; + t.start(); + // std::shared_ptr<std::nullptr_t> logTime = std::make_shared<std::nullptr_t>(nullptr, [t]() { + // qDebug() << "HttpServer::GET_pixels: elapsed" << t.nsecsElapsed() / 1000 << "(us)"; + // }); + const std::shared_ptr<std::nullptr_t> logTime{nullptr, [t](auto unused_ptr) { + qDebug() << "HttpServer::GET_pixels: elapsed" + << t.nsecsElapsed() / 1000 << "(us)"; + }}; + + Image img; + { + // const auto sharedCam = m_camera.lock(); + // FIME: don't cast anything, use interface + // auto cam = dynamic_cast<VeyeIMX287m *>(sharedCam.get()); + auto cam = dynamic_cast<VeyeIMX287m *>(m_camera); + + if (!cam) { + qWarning() << "NO CAM"; + return QHttpServerResponse::StatusCode::ServiceUnavailable; + } + + // yeaah + // ::img = &img; + if (!cam->getImage(img)) { + qWarning() << "cannot get image"; + return QHttpServerResponse::StatusCode::ServiceUnavailable; + } + + // ::pixels = std::move(img.pixels()); + ++cam->processedCounter; + } + + // FIXME: not thread-safe, don't use this static shared_ptr at all + const auto pixels = img.sharedPixels(); + // const auto lines = pixelsToLines(::pixels); + const auto lines = pixelsToLines(*pixels); + + // qt json does not allow to limit double precision, so using rapidjson + rapidjson::Document jd; + jd.SetObject(); + auto &al = jd.GetAllocator(); + + const auto nan2zero = [](const auto &value) { return qIsNaN(value) ? 0 : value; }; + + rapidjson::Value rjPixels{rapidjson::kArrayType}; + + for (size_t i = 0; i < img_width; ++i) { + rjPixels.PushBack(nan2zero(pixels->pixels[i]), al); + } + + rapidjson::Value rjLines{rapidjson::kArrayType}; + + for (const auto &l : lines) { + rapidjson::Value rjLineP1{rapidjson::kArrayType}; + rjLineP1.PushBack(nan2zero(l.p1().x()), al).PushBack(nan2zero(l.p1().y()), al); + rapidjson::Value rjLineP2{rapidjson::kArrayType}; + rjLineP2.PushBack(nan2zero(l.p2().x()), al).PushBack(nan2zero(l.p2().y()), al); + rapidjson::Value rjLinePoints{rapidjson::kArrayType}; + rjLinePoints.PushBack(rjLineP1, al).PushBack(rjLineP2, al); + + rjLines.PushBack(rjLinePoints, al); + } + + // jd.AddMember("encoderPosition", qint64{encoder.position()}); + // FIXME: get prom pixels struct + jd.AddMember("measurementCounter", img.counters.measurementCounter, al); + jd.AddMember("timestampUs", img.counters.timestampUs, al); + jd.AddMember("pixels", rjPixels.Move(), al); + jd.AddMember("lines", rjLines.Move(), al); + + rapidjson::StringBuffer buffer; + rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); + writer.SetMaxDecimalPlaces(2); + jd.Accept(writer); + const QString res{(const char *) buffer.GetString()}; + // qDebug() << "size:" << res.size(); + // qDebug().noquote() << "ret pix"; + + return QHttpServerResponse{res}; +} diff --git a/src/httpservice.h b/src/httpservice.h index 2b0e7f5..0fbd54d 100644 --- a/src/httpservice.h +++ b/src/httpservice.h @@ -2,22 +2,41 @@ // qt #include <QHostAddress> +#include <QHttpServerResponse> +// #include <QObject> class ICamera; +class QHttpServer; -class HttpServer +class HttpServer // : public QObject { + // Q_OBJECT + +private: + struct Stats + { + uint64_t GET_pixels_us{0}; + } m_stats{0}; + public: static constexpr auto DefaultAddress = QHostAddress::Any; - static constexpr uint16_t DefaultPort{8081}; + static constexpr uint16_t DefaultPort{8080}; public: - explicit HttpServer(const std::shared_ptr<ICamera> &camera, + explicit HttpServer(ICamera *camera, + // QObject *parent = nullptr, const QHostAddress &address = DefaultAddress, const uint16_t port = DefaultPort); + // TODO: methods starting with GET_/POST_ will be routed automatically +public: + QHttpServerResponse GET_pixels(); + private: - std::shared_ptr<ICamera> m_camera{nullptr}; + // std::weak_ptr<ICamera> m_camera; + ICamera *m_camera{nullptr}; QHostAddress m_address{DefaultAddress}; uint16_t m_port{DefaultPort}; + + std::shared_ptr<QHttpServer> m_server; }; |
