From d12498504c279a0a85bbfb024f7903e34dbe07db Mon Sep 17 00:00:00 2001 From: Nikita Kostovsky Date: Fri, 21 Feb 2025 07:27:00 +0100 Subject: broken img calc; change dir struct --- CMakeLists.txt | 49 +-- LibCamera.cpp | 267 -------------- LibCamera.h | 91 ----- fuck_intel.h | 12 - genetic_algos.cpp | 1 - genetic_algos.h | 174 --------- httpservice.cpp | 1 - httpservice.h | 203 ----------- imagealgos.cpp | 478 ------------------------ imagealgos.h | 19 - macro.h | 17 - main.cpp | 795 ---------------------------------------- printerclient.cpp | 86 ----- printerclient.h | 30 -- profile.cpp | 155 -------- profile.h | 29 -- rotaryencoder.cpp | 125 ------- rotaryencoder.h | 20 -- src/LibCamera.cpp | 267 ++++++++++++++ src/LibCamera.h | 91 +++++ src/camera/icamera.h | 4 +- src/camera/innomakerov9281.cpp | 180 +++++++--- src/camera/innomakerov9281.h | 11 +- src/camera/ov9281.cpp | 12 +- src/constants.h | 2 +- src/fuck_intel.h | 12 + src/genetic_algos.cpp | 1 + src/genetic_algos.h | 174 +++++++++ src/httpservice.cpp | 1 + src/httpservice.h | 203 +++++++++++ src/image.cpp | 74 +++- src/image.h | 18 +- src/imagealgos.cpp | 465 ++++++++++++++++++++++++ src/imagealgos.h | 18 + src/macro.h | 17 + src/main.cpp | 798 +++++++++++++++++++++++++++++++++++++++++ src/printerclient.cpp | 86 +++++ src/printerclient.h | 30 ++ src/profile.cpp | 155 ++++++++ src/profile.h | 29 ++ src/rotaryencoder.cpp | 125 +++++++ src/rotaryencoder.h | 20 ++ 42 files changed, 2730 insertions(+), 2615 deletions(-) delete mode 100644 LibCamera.cpp delete mode 100644 LibCamera.h delete mode 100644 fuck_intel.h delete mode 100644 genetic_algos.cpp delete mode 100644 genetic_algos.h delete mode 100644 httpservice.cpp delete mode 100644 httpservice.h delete mode 100644 imagealgos.cpp delete mode 100644 imagealgos.h delete mode 100644 macro.h delete mode 100644 main.cpp delete mode 100644 printerclient.cpp delete mode 100644 printerclient.h delete mode 100644 profile.cpp delete mode 100644 profile.h delete mode 100644 rotaryencoder.cpp delete mode 100644 rotaryencoder.h create mode 100644 src/LibCamera.cpp create mode 100644 src/LibCamera.h create mode 100644 src/fuck_intel.h create mode 100644 src/genetic_algos.cpp create mode 100644 src/genetic_algos.h create mode 100644 src/httpservice.cpp create mode 100644 src/httpservice.h create mode 100644 src/imagealgos.cpp create mode 100644 src/imagealgos.h create mode 100644 src/macro.h create mode 100644 src/main.cpp create mode 100644 src/printerclient.cpp create mode 100644 src/printerclient.h create mode 100644 src/profile.cpp create mode 100644 src/profile.h create mode 100644 src/rotaryencoder.cpp create mode 100644 src/rotaryencoder.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 01bf27f..8826e5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR arm) set(CMAKE_INCLUDE_CURRENT_DIR ON) -# set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC OFF) # set(TARGET_SYSROOT /home/nikita/rpi/rpi-sysroot) @@ -27,6 +27,9 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${TARGET_SYSROOT}/usr/include") set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}") +# NOTE: in case of header issues during inno-maker driver compilation - try to +# reinstall linux-headers-, like this +# sudo apt install linux-headers-6.6.74+rpt-rpi-v8 --reinstall # NOTE: on your rpi you'll need to install # libcamera-dev, libcamera-ipa (TODO: check if ipa is needed) @@ -61,6 +64,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}") # # export this path for qtcreator: # LD_LIBRARY_PATH=/home/nikita/rpi/rpi-sysroot/usr/lib/aarch64-linux-gnu/:$LD_LIBRARY_PATH +set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}/usr) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) @@ -97,40 +101,15 @@ find_package(TBB REQUIRED) qt_standard_project_setup(REQUIRES 6.4) -include_directories(. "${CAMERA_INCLUDE_DIRS}" src) +include_directories("${CAMERA_INCLUDE_DIRS}" src) # libpistache pkg_check_modules(Pistache REQUIRED IMPORTED_TARGET libpistache) # add_executable(${PROJECT_NAME} -qt_add_executable(apporpheus - httpservice.h - httpservice.cpp - imagealgos.h - imagealgos.cpp - macro.h - main.cpp - LibCamera.h - LibCamera.cpp - genetic_algos.h - genetic_algos.cpp - rotaryencoder.h - rotaryencoder.cpp - printerclient.h - printerclient.cpp - fuck_intel.h - src/pixels.h src/pixels.cpp - src/constants.h - src/typedefs.h - src/dumps.h src/dumps.cpp - src/calibration.h src/calibration.cpp - profile.h profile.cpp - src/camera/ov9281.h src/camera/ov9281.cpp - src/image.h src/image.cpp - src/laser.h src/laser.cpp - src/camera/innomakerov9281.h src/camera/innomakerov9281.cpp - src/camera/icamera.h -) +file(GLOB_RECURSE SOURCES src/*.h src/*.cpp src/*.c src/*.qrc) +message("sources: ${SOURCES}") +qt_add_executable(apporpheus ${SOURCES}) target_link_libraries(app${PROJECT_NAME} PRIVATE "${LIBCAMERA_LIBRARIES}" @@ -150,11 +129,11 @@ install(TARGETS app${PROJECT_NAME} ) if ("${CMAKE_BUILD_TYPE}" MATCHES "Release") - # message("it's Debug") + message("it's Release") + add_compile_options(-Ofast -fno-unroll-loops -Wall) + # add_compile_options(-Wall -Wextra -Wpedantic) + # add_compile_options(-O3 -Wall) else() - #message("it's not Debug") + message("it's not Release") endif() - -# add_compile_options(-Wall -Wextra -Wpedantic) -add_compile_options(-Ofast -fno-unroll-loops -Wall) diff --git a/LibCamera.cpp b/LibCamera.cpp deleted file mode 100644 index 1e68228..0000000 --- a/LibCamera.cpp +++ /dev/null @@ -1,267 +0,0 @@ -// #include "LibCamera.h" - -// using namespace std::placeholders; - -// int LibCamera::initCamera() { -// int ret; -// cm = std::make_unique(); -// ret = cm->start(); -// if (ret){ -// std::cout << "Failed to start camera manager: " -// << ret << std::endl; -// return ret; -// } -// cameraId = cm->cameras()[0]->id(); -// camera_ = cm->get(cameraId); -// if (!camera_) { -// std::cerr << "Camera " << cameraId << " not found" << std::endl; -// return 1; -// } - -// if (camera_->acquire()) { -// std::cerr << "Failed to acquire camera " << cameraId -// << std::endl; -// return 1; -// } -// camera_acquired_ = true; -// return 0; -// } - -// char * LibCamera::getCameraId(){ -// return cameraId.data(); -// } - -// void LibCamera::configureStill(int width, int height, PixelFormat format, int buffercount, int rotation) { -// printf("Configuring still capture...\n"); -// config_ = camera_->generateConfiguration({ StreamRole::StillCapture }); -// if (width && height) { -// libcamera::Size size(width, height); -// config_->at(0).size = size; -// } -// config_->at(0).pixelFormat = format; -// if (buffercount) -// config_->at(0).bufferCount = buffercount; -// Transform transform = Transform::Identity; -// bool ok; -// Transform rot = transformFromRotation(rotation, &ok); -// if (!ok) -// throw std::runtime_error("illegal rotation value, Please use 0 or 180"); -// transform = rot * transform; -// if (!!(transform & Transform::Transpose)) -// throw std::runtime_error("transforms requiring transpose not supported"); -// // FIXME: update -// // config_->transform = transform; - -// CameraConfiguration::Status validation = config_->validate(); -// if (validation == CameraConfiguration::Invalid) -// throw std::runtime_error("failed to valid stream configurations"); -// else if (validation == CameraConfiguration::Adjusted) -// std::cout << "Stream configuration adjusted" << std::endl; - -// printf("Still capture setup complete\n"); -// } - -// int LibCamera::startCamera() { -// int ret; -// ret = camera_->configure(config_.get()); -// if (ret < 0) { -// std::cout << "Failed to configure camera" << std::endl; -// return ret; -// } - -// camera_->requestCompleted.connect(this, &LibCamera::requestComplete); - -// allocator_ = std::make_unique(camera_); - -// return startCapture(); -// } - -// int LibCamera::startCapture() { -// int ret; -// unsigned int nbuffers = UINT_MAX; -// for (StreamConfiguration &cfg : *config_) { -// ret = allocator_->allocate(cfg.stream()); -// if (ret < 0) { -// std::cerr << "Can't allocate buffers" << std::endl; -// return -ENOMEM; -// } - -// unsigned int allocated = allocator_->buffers(cfg.stream()).size(); -// nbuffers = std::min(nbuffers, allocated); -// } - -// for (unsigned int i = 0; i < nbuffers; i++) { -// std::unique_ptr request = camera_->createRequest(); -// if (!request) { -// std::cerr << "Can't create request" << std::endl; -// return -ENOMEM; -// } - -// for (StreamConfiguration &cfg : *config_) { -// Stream *stream = cfg.stream(); -// const std::vector> &buffers = -// allocator_->buffers(stream); -// const std::unique_ptr &buffer = buffers[i]; - -// ret = request->addBuffer(stream, buffer.get()); -// if (ret < 0) { -// std::cerr << "Can't set buffer for request" -// << std::endl; -// return ret; -// } -// for (const FrameBuffer::Plane &plane : buffer->planes()) { -// void *memory = mmap(NULL, plane.length, PROT_READ, MAP_SHARED, -// plane.fd.get(), 0); -// mappedBuffers_[plane.fd.get()] = -// std::make_pair(memory, plane.length); -// } -// } - -// requests_.push_back(std::move(request)); -// } - -// ret = camera_->start(&this->controls_); -// // ret = camera_->start(); -// if (ret) { -// std::cout << "Failed to start capture" << std::endl; -// return ret; -// } -// controls_.clear(); -// camera_started_ = true; -// for (std::unique_ptr &request : requests_) { -// ret = queueRequest(request.get()); -// if (ret < 0) { -// std::cerr << "Can't queue request" << std::endl; -// camera_->stop(); -// return ret; -// } -// } -// viewfinder_stream_ = config_->at(0).stream(); -// return 0; -// } - -// void LibCamera::StreamDimensions(Stream const *stream, uint32_t *w, uint32_t *h, uint32_t *stride) const -// { -// StreamConfiguration const &cfg = stream->configuration(); -// if (w) -// *w = cfg.size.width; -// if (h) -// *h = cfg.size.height; -// if (stride) -// *stride = cfg.stride; -// } - -// Stream *LibCamera::VideoStream(uint32_t *w, uint32_t *h, uint32_t *stride) const -// { -// StreamDimensions(viewfinder_stream_, w, h, stride); -// return viewfinder_stream_; -// } - -// int LibCamera::queueRequest(Request *request) { -// std::lock_guard stop_lock(camera_stop_mutex_); -// if (!camera_started_) -// return -1; -// { -// std::lock_guard lock(control_mutex_); -// request->controls() = std::move(controls_); -// } -// return camera_->queueRequest(request); -// } - -// void LibCamera::requestComplete(Request *request) { -// if (request->status() == Request::RequestCancelled) -// return; -// processRequest(request); -// } - -// void LibCamera::processRequest(Request *request) { -// requestQueue.push(request); -// } - -// void LibCamera::returnFrameBuffer(LibcameraOutData frameData) { -// uint64_t request = frameData.request; -// Request * req = (Request *)request; -// req->reuse(Request::ReuseBuffers); -// queueRequest(req); -// } - -// bool LibCamera::readFrame(LibcameraOutData *frameData){ -// std::lock_guard lock(free_requests_mutex_); -// // int w, h, stride; -// if (!requestQueue.empty()){ -// Request *request = this->requestQueue.front(); - -// const Request::BufferMap &buffers = request->buffers(); -// for (auto it = buffers.begin(); it != buffers.end(); ++it) { -// FrameBuffer *buffer = it->second; -// for (unsigned int i = 0; i < buffer->planes().size(); ++i) { -// const FrameBuffer::Plane &plane = buffer->planes()[i]; -// const FrameMetadata::Plane &meta = buffer->metadata().planes()[i]; - -// void *data = mappedBuffers_[plane.fd.get()].first; -// int length = std::min(meta.bytesused, plane.length); - -// frameData->size = length; -// frameData->imageData = (uint8_t *)data; -// } -// } -// this->requestQueue.pop(); -// frameData->request = (uint64_t)request; -// return true; -// } else { -// Request *request = nullptr; -// frameData->request = (uint64_t)request; -// return false; -// } -// } - -// void LibCamera::set(ControlList controls){ -// std::lock_guard lock(control_mutex_); -// this->controls_ = std::move(controls); -// } - -// int LibCamera::resetCamera(int width, int height, PixelFormat format, int buffercount, int rotation) { -// stopCamera(); -// configureStill(width, height, format, buffercount, rotation); -// return startCamera(); -// } - -// void LibCamera::stopCamera() { -// if (camera_){ -// { -// std::lock_guard lock(camera_stop_mutex_); -// if (camera_started_){ -// if (camera_->stop()) -// throw std::runtime_error("failed to stop camera"); -// camera_started_ = false; -// } -// } -// camera_->requestCompleted.disconnect(this, &LibCamera::requestComplete); -// } -// while (!requestQueue.empty()) -// requestQueue.pop(); - -// for (auto &iter : mappedBuffers_) -// { -// std::pair pair_ = iter.second; -// munmap(std::get<0>(pair_), std::get<1>(pair_)); -// } - -// mappedBuffers_.clear(); - -// requests_.clear(); - -// allocator_.reset(); - -// controls_.clear(); -// } - -// void LibCamera::closeCamera(){ -// if (camera_acquired_) -// camera_->release(); -// camera_acquired_ = false; - -// camera_.reset(); - -// cm.reset(); -// } diff --git a/LibCamera.h b/LibCamera.h deleted file mode 100644 index 89cf835..0000000 --- a/LibCamera.h +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include -// #include - -#ifdef signals -#error ("don't include this file after any qt files") -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -// using namespace libcamera; - -// typedef struct { -// uint8_t *imageData; -// uint32_t size; -// uint64_t request; -// } LibcameraOutData; - -// class LibCamera { -// public: -// LibCamera(){}; -// ~LibCamera(){}; - -// int initCamera(); -// void configureStill(int width, int height, PixelFormat format, int buffercount, int rotation); -// int startCamera(); -// int resetCamera(int width, int height, PixelFormat format, int buffercount, int rotation); -// bool readFrame(LibcameraOutData *frameData); -// void returnFrameBuffer(LibcameraOutData frameData); - -// void set(ControlList controls); -// void stopCamera(); -// void closeCamera(); - -// Stream *VideoStream(uint32_t *w, uint32_t *h, uint32_t *stride) const; -// char * getCameraId(); - -// private: -// int startCapture(); -// int queueRequest(Request *request); -// void requestComplete(Request *request); -// void processRequest(Request *request); - -// void StreamDimensions(Stream const *stream, uint32_t *w, uint32_t *h, uint32_t *stride) const; - -// unsigned int cameraIndex_; -// uint64_t last_; -// std::unique_ptr cm; -// std::shared_ptr camera_; -// bool camera_acquired_ = false; -// bool camera_started_ = false; -// std::unique_ptr config_; -// std::unique_ptr allocator_; -// std::vector> requests_; -// // std::map stream_; -// std::map> mappedBuffers_; - -// std::queue requestQueue; - -// ControlList controls_; -// std::mutex control_mutex_; -// std::mutex camera_stop_mutex_; -// std::mutex free_requests_mutex_; - -// Stream *viewfinder_stream_ = nullptr; -// std::string cameraId; -// }; diff --git a/fuck_intel.h b/fuck_intel.h deleted file mode 100644 index 6d9f9d5..0000000 --- a/fuck_intel.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -// include this file before including any qt files if you have emit conflict -// with tbb - -#ifdef emit -#undef emit -#include -#define emit -#else -#include -#endif diff --git a/genetic_algos.cpp b/genetic_algos.cpp deleted file mode 100644 index 1962768..0000000 --- a/genetic_algos.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "genetic_algos.h" diff --git a/genetic_algos.h b/genetic_algos.h deleted file mode 100644 index 8dea732..0000000 --- a/genetic_algos.h +++ /dev/null @@ -1,174 +0,0 @@ -#pragma once - -// #define _USE_MATH_DEFINES -#include -#include -#include -#include -// TODO: - -/* - -a, sigma - -*/ - -// #define POP_SIZE 30 -// #define NUM_IT 25 -// #define COL_SIZE 16 - -// using Column = double; - -static std::random_device random_device; -static std::mt19937 gen(random_device()); -static std::uniform_real_distribution<> dis01(0., 1.); -static std::uniform_real_distribution<> disDelta2(-2., 2.); - -template -struct Item { - double a, sigma; // E, sigma = sqrt(D) - T* column; - - double W; - - Item() {} - - Item(double a_, double s, T * column_) : a(a_), sigma(s), column(column_) { - update(); - } - - double gauss(double t) { - return std::exp(std::pow((t - a) / sigma, - 2) / - 2) / - std::sqrt(2 * std::numbers::pi) / - sigma; - } - - double F() { // objective function - double s = 0; - int x0 = std::floor(.5 + a - 3 * sigma); - double x1 = std::round(a + 3 * sigma); - - for (int j = x0; j <= x1; j++) - s += std::pow(gauss(j) - column[j], 2); - - return s; - } - - void update() { - W = F(); - } - - // action - - Item move() { - double a1 = a + disDelta2(gen); - double sigma1 = sigma * (1 + disDelta2(gen)); // a: ~ +- 2 pixel - - return Item(a1, sigma1, column); - } - - // a = q * a1 + (1 - q) * a2, sigma = q * s1 + (1 - q) * s2 - Item crossover(const Item& other) { - double q = dis01(gen); - double a_ = q * a + (1 - q) * other.a; - double sigma_ = q * sigma + (1 - q) * other.sigma; - - return Item(a_, sigma_, column); - } -}; - -template -struct Algo { - T * column; - using I = Item; - std::vector population; - std::uniform_real_distribution disA { 0., double(1.) }; - - double targetW; - - Algo(T * column_): column(column_) { - init(); - } - - I getNewItem() { - double a = rand() % (COL_SIZE * 1000) / 1000.; - double sigma = rand() % (COL_SIZE * 100) / 1000.; - - return I(a, sigma, column); - } - - void init() { - for (size_t i = 0; i < POP_SIZE; i++) { - population.push_back(getNewItem()); - } - } - - bool stopCondition() { - // return population[0].W <= targetW; - return population[0].W <= maxW; - } - - I run() { - for (int it = 0; it < NUM_IT; it++) { - work(); - - // if (stopCondition()) - // break; - } - - return population[0]; - } - - void work() { - move(); - crossover(); - addNew(); - - sort(); - - remove(); - } - - void sort() { - // sort vector ASC - std::sort(population.begin(), population.end(), - [](const auto & a, const auto & b) - { - return a.W < b.W; - }); - } - - void remove() { - population.erase(population.begin() + POP_SIZE, population.end()); - } - - void move() { - for (I& item : population) { - population.push_back(item.move()); - } - } - - void addNew() { - for (int i = 0; i < 5; i++) { - population.push_back(getNewItem()); - } - } - - void crossover() { - for (int i1 = 0; i1 < 4; i1++) { - - for (int i2 = i1 + 1; i2 < 4; i2++) { - I& x1 = population[i1]; - I& x2 = population[i2]; - // a = q * a1 + (1 - q) * a2, sigma = q * s1 + (1 - q) * s2 - population.push_back(x1.crossover(x2)); - } - } - } -}; diff --git a/httpservice.cpp b/httpservice.cpp deleted file mode 100644 index 7fa72fb..0000000 --- a/httpservice.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "httpservice.h" diff --git a/httpservice.h b/httpservice.h deleted file mode 100644 index a6c3f76..0000000 --- a/httpservice.h +++ /dev/null @@ -1,203 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -#include "imagealgos.h" - -using namespace Pistache; - -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; - -class HttpService -{ -public: - HttpService(Address addr) - : httpEndpoint(std::make_shared(addr)) - , desc("Banking API", "0.1") - { } - - void init(size_t thr = 2) - { - auto opts = Http::Endpoint::options() - .threads(static_cast(thr)); - httpEndpoint->init(opts); - createDescription(); - } - - void start() - { - router.initFromDescription(desc); - - Rest::Swagger swagger(desc); - swagger - .uiPath("/doc") - .uiDirectory("/home/user/swagger-ui/dist") - .apiPath("/banker-api.json") - .serializer(&Rest::Serializer::rapidJson) - .install(router); - - httpEndpoint->setHandler(router.handler()); - httpEndpoint->serve(); - } - -private: - void createDescription() - { - desc - .info() - .license("Apache", "http://www.apache.org/licenses/LICENSE-2.0"); - - auto backendErrorResponse = desc.response(Http::Code::Internal_Server_Error, "An error occured with the backend"); - - desc - .schemes(Rest::Scheme::Http) - .basePath("/v1") - .produces(MIME(Application, Json)) - .consumes(MIME(Application, Json)); - - // desc - // .route(desc.get("/ready")) - // .bind(&Generic::handleReady) - // .response(Http::Code::Ok, "Response to the /ready call") - // .hide(); - - auto versionPath = desc.path("/v1"); - - auto sensorPath = versionPath.path("/sensor"); - - sensorPath - .route(desc.get("/image")) - .bind(&HttpService::image, this) - .produces(MIME(Image, Png)) - .response(Http::Code::Ok, "Image from sensor"); - - // tmp - sensorPath - .route(desc.get("/image2")) - .bind(&HttpService::image, this) - .produces(MIME(Image, Png)) - .response(Http::Code::Ok, "Image from sensor"); - - sensorPath - .route(desc.get("/params"), "Retrive sensor parameters") - .bind(&HttpService::get_sensorParams, this) - .produces(MIME(Application, Plain)) - .response(Http::Code::Ok, "Parameter value") - .response(backendErrorResponse);; - - sensorPath - .route(desc.get("/:param"), "Retrive sensor parameter") - .bind(&HttpService::get_sensorParam, this) - .produces(MIME(Application, Json)) - .parameter("param", "The name of the parameter to retrieve") - .response(Http::Code::Ok, "Parameter value") - .response(backendErrorResponse);; - - sensorPath - .route(desc.post("/:param"), "Set sensor parameter") - .bind(&HttpService::set_sensorParam, this) - .produces(MIME(Application, Plain)) - .consumes(MIME(Application, Plain)) - .response(Http::Code::Ok, "Setting parameter result"); - - - auto accountsPath = versionPath.path("/accounts"); - - accountsPath - .route(desc.get("/all")) - .bind(&HttpService::retrieveAllAccounts, this) - .produces(MIME(Application, Json), MIME(Application, Xml)) - .response(Http::Code::Ok, "The list of all account"); - - accountsPath - .route(desc.get("/:name"), "Retrieve an account") - .bind(&HttpService::retrieveAccount, this) - .produces(MIME(Application, Json)) - .parameter("name", "The name of the account to retrieve") - .response(Http::Code::Ok, "The requested account") - .response(backendErrorResponse); - - accountsPath - .route(desc.post("/:name"), "Create an account") - .bind(&HttpService::createAccount, this) - .produces(MIME(Application, Json)) - .consumes(MIME(Application, Json)) - .parameter("name", "The name of the account to create") - .response(Http::Code::Ok, "The initial state of the account") - .response(backendErrorResponse); - auto accountPath = accountsPath.path("/:name"); - accountPath.parameter("name", "The name of the account to operate on"); - - accountPath - .route(desc.post("/budget"), "Add budget to the account") - .bind(&HttpService::creditAccount, this) - .produces(MIME(Application, Json)) - .response(Http::Code::Ok, "Budget has been added to the account") - .response(backendErrorResponse); - } - - void retrieveAllAccounts(const Rest::Request&, Http::ResponseWriter response) - { - response.send(Http::Code::Ok, - "No Account", - { Http::Mime::Type::Text, Http::Mime::Subtype::Plain} ); - } - - void retrieveAccount(const Rest::Request&, Http::ResponseWriter response) - { - response.send(Http::Code::Ok, "The bank is closed, come back later"); - } - - void createAccount(const Rest::Request&, Http::ResponseWriter response) - { - response.send(Http::Code::Ok, "The bank is closed, come back later"); - } - - void creditAccount(const Rest::Request&, Http::ResponseWriter response) - { - response.send(Http::Code::Ok, "The bank is closed, come back later"); - } - - void image(const Rest::Request&, Http::ResponseWriter response) - { - // FIXME: image should be valid - std::lock_guard lg(pgm_image_mtx); - // char data[pgm_image_size]; - // memcpy(data, pgm_image, pgm_image_size); - std::cout << "send bytes: " << pgm_image_size << std::endl; - - auto res = response.send(Http::Code::Ok, - (const char*)pgm_image, pgm_image_size, - Http::Mime::MediaType { "image/pgm" }); - // { Http::Mime::Type::Image, Http::Mime::Subtype::Png }); - - res.then([](ssize_t bytes) - { std::cout << bytes << " bytes have been sent\n"; }, - Async::NoExcept); - } - - void get_sensorParam(const Rest::Request&, Http::ResponseWriter response) - { - response.send(Http::Code::Ok, std::to_string(123)); - } - - void get_sensorParams(const Rest::Request&, Http::ResponseWriter response) - { - response.send(Http::Code::Ok, std::to_string(123)); - } - - void set_sensorParam(const Rest::Request&, Http::ResponseWriter response) - { - response.send(Http::Code::Ok, std::to_string(123)); - } - - std::shared_ptr httpEndpoint; - Rest::Description desc; - Rest::Router router; -}; diff --git a/imagealgos.cpp b/imagealgos.cpp deleted file mode 100644 index 801c588..0000000 --- a/imagealgos.cpp +++ /dev/null @@ -1,478 +0,0 @@ -#include "imagealgos.h" - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "macro.h" - -uint8_t pgm_image[64 + img_width * img_height * sizeof(uint8_t)]; -size_t pgm_image_size = 0; -std::mutex pgm_image_mtx; - -template -T median3(const T &a, const T &b, const T &c) -{ - using namespace std; - return max(min(a, b), min(max(a, b), c)); -} - -size_t pgm_save(std::shared_ptr img, FILE *outfile, bool really_save) -{ - std::lock_guard lg(pgm_image_mtx); - - size_t n = 0; - - // n += fprintf(outfile, "P5\n%d %d\n%d\n", - // img->width, img->height, 0xFF); - n += sprintf((char*)pgm_image, "P5\n%d %d\n%d\n", - img->width, img->height, 0xFF); - - // for (size_t i = 0; i < img->width * img->height; ++i) - for (size_t i = 0; i < img_width * img_height; ++i) - { - uint8_t *pixels = (uint8_t *) img->data; - // const auto p = pixels[i]; - // uint8_t value = (pixels[i] & 0xFF00) >> 8; - uint8_t value = pixels[i]; - - // n += fwrite(&value, 1, 1, outfile); - memcpy((void*)(pgm_image + n), &value, sizeof(value)); - n += sizeof(value); - } - - pgm_image_size = n; - - // std::cout << "size is " << n << std::endl; - - if (really_save) - { - if (outfile) { - fwrite(pgm_image, 1, pgm_image_size, outfile); - fflush(outfile); - - } else { - std::cerr << __func__ << ": output filename is nullptr, cannot save" - << std::endl; - } - } - - // std::cout << "written pgm image" << std::endl; - return n; -} - -// void unpack_10bit(uint8_t const *src, Image const &image, uint16_t *dest) -// { -// unsigned int w_align = image.width & ~3; -// for (unsigned int y = 0; y < image.height; y++, src += image.stride) -// { -// uint8_t const *ptr = src; -// unsigned int x; -// for (x = 0; x < w_align; x += 4, ptr += 5) -// { -// *dest++ = (ptr[0] << 2) | ((ptr[4] >> 0) & 3); -// *dest++ = (ptr[1] << 2) | ((ptr[4] >> 2) & 3); -// *dest++ = (ptr[2] << 2) | ((ptr[4] >> 4) & 3); -// *dest++ = (ptr[3] << 2) | ((ptr[4] >> 6) & 3); -// } -// for (; x < image.width; x++) -// *dest++ = (ptr[x & 3] << 2) | ((ptr[4] >> ((x & 3) << 1)) & 3); -// } -// } - -// void unpack_16bit(uint8_t const *src, Image const &image, uint16_t *dest) -// { -// start_timer(unpack_16bit); -// /* Assume the pixels in memory are already in native byte order */ -// unsigned int w = image.width; - -// for (unsigned int y = 0; y < image.height; y++) -// { -// memcpy(dest, src, 2 * w); -// dest += w; -// src += image.stride; -// } -// stop_timer(unpack_16bit); -// } - -template -constexpr size_t mysize(T (&)[N]) { return N; } - -// void smooth_column(uint8_t (&column)[]) -// { -// for (size_t i = 1; i < img_height - 1; ++i) { -// column[i] = median3(column[i - 1], column[i], column[i + 1]); -// } -// } - -// float process_column(uint8_t (&column)[]) -// { -// std::cout << "aaaaaaaaaaa\n"; -// start_timer(process_column); - -// float result = std::numeric_limits::quiet_NaN(); - -// constexpr uint32_t signalThreshold = 900; // = SKO * sqrt(patternSize) -// static constexpr uint32_t patternOffset = patternSize - ((patternSize % 2 == 1) ? 1 : 0); -// const uint32_t correlationSize = img_height - patternSize + ((patternSize % 2 == 1) ? 1 : 0); -// uint32_t correlation[img_height]; -// uint32_t integralSum[img_height]; -// uint32_t maxSum = signalThreshold * 50; -// uint32_t x1 = 0; -// int32_t y1 = 0; -// int32_t y2 = 0; - -// memset(correlation, 0, img_height * sizeof(correlation[0])); -// integralSum[0] = 0; - -// for (uint32_t i = 1; i < img_height; ++i) { -// // if (column[i + 0] < 100) { column[i + 0] = 0; } integralSum[i + 0] = column[i + 0] / 256 + integralSum[i + 0 - 1]; -// // if (column[i + 1] < 100) { column[i + 1] = 0; } integralSum[i + 1] = column[i + 1] / 256 + integralSum[i + 1 - 1]; -// // if (column[i + 2] < 100) { column[i + 2] = 0; } integralSum[i + 2] = column[i + 2] / 256 + integralSum[i + 2 - 1]; -// // if (column[i + 3] < 100) { column[i + 3] = 0; } integralSum[i + 3] = column[i + 3] / 256 + integralSum[i + 3 - 1]; -// // if (column[i + 4] < 100) { column[i + 4] = 0; } integralSum[i + 4] = column[i + 4] / 256 + integralSum[i + 4 - 1]; -// // if (column[i + 5] < 100) { column[i + 5] = 0; } integralSum[i + 5] = column[i + 5] / 256 + integralSum[i + 5 - 1]; -// // if (column[i + 6] < 100) { column[i + 6] = 0; } integralSum[i + 6] = column[i + 6] / 256 + integralSum[i + 6 - 1]; -// // if (column[i + 7] < 100) { column[i + 7] = 0; } integralSum[i + 7] = column[i + 7] / 256 + integralSum[i + 7 - 1]; -// // if (column[i + 8] < 100) { column[i + 8] = 0; } integralSum[i + 8] = column[i + 8] / 256 + integralSum[i + 8 - 1]; -// // if (column[i + 9] < 100) { column[i + 9] = 0; } integralSum[i + 9] = column[i + 9] / 256 + integralSum[i + 9 - 1]; -// // if (column[i + 10] < 100) { column[i + 10] = 0; } integralSum[i + 10] = column[i + 10] / 256 + integralSum[i + 10 - 1]; -// // if (column[i + 11] < 100) { column[i + 11] = 0; } integralSum[i + 11] = column[i + 11] / 256 + integralSum[i + 11 - 1]; -// // if (column[i + 12] < 100) { column[i + 12] = 0; } integralSum[i + 12] = column[i + 12] / 256 + integralSum[i + 12 - 1]; -// // if (column[i + 13] < 100) { column[i + 13] = 0; } integralSum[i + 13] = column[i + 13] / 256 + integralSum[i + 13 - 1]; -// // if (column[i + 14] < 100) { column[i + 14] = 0; } integralSum[i + 14] = column[i + 14] / 256 + integralSum[i + 14 - 1]; -// // if (column[i + 15] < 100) { column[i + 15] = 0; } integralSum[i + 15] = column[i + 15] / 256 + integralSum[i + 15 - 1]; - -// if (column[i] < 100) { -// column[i] = 0; -// } -// integralSum[i] = column[i] + integralSum[i - 1]; -// } - -// // maxSum = 0 ; -// // size_t maxIdx { 0 }; - -// // for (size_t i = 0; i < img_height - patternSize; ++i) { -// // const auto sum = integralSum[i + patternSize] - integralSum[i]; -// // if (sum > maxSum) { -// // maxSum = sum; -// // maxIdx = i; -// // } -// // } - -// // Algo genetic(column + maxIdx); -// // // std::cout << "maxIdx " << maxIdx << std::endl; - -// // // return maxIdx + genetic.run().a; -// // return 500; -// // return img_height - maxIdx - genetic.run().a; - -// for (uint32_t i = 0; i < correlationSize; ++i) -// correlation[i + patternSize / 2] = column[i + patternSize / 2] / 256 -// * (integralSum[i + patternOffset] - integralSum[i]); - -// for (uint32_t i = 3; i < img_height - 2; ++i) { -// const auto sum = correlation[i - 1] + correlation[i] + correlation[i + 1]; - -// if (sum > maxSum) { -// const int32_t rioux0 = int32_t(correlation[i - 2 - 1] + correlation[i - 1 - 1]) -// - int32_t(correlation[i + 1 - 1] + correlation[i + 2 - 1]); - -// if (rioux0 < 0) { -// const int32_t rioux1 = int32_t(correlation[i - 2] + correlation[i - 1]) -// - int32_t(correlation[i + 1] + correlation[i + 2]); - -// if (rioux1 >= 0) { -// x1 = i - 1; -// y1 = rioux0; -// y2 = rioux1; -// maxSum = sum; -// } -// } -// } -// } - -// result = (y2 != y1) ? (float(x1) - (float(y1) / (y2 - y1))) -// : std::numeric_limits::quiet_NaN(); - -// static bool result_done = false; -// if (!result_done) { -// std::cout << "result " << result << std::endl; -// result_done = true; -// } -// // std::cout << "result is '" << result << "'\n"; - -// // stop_timer(process_column); - -// return result; - -// // center of mass -// #if 0 -// auto max_el = std::max_element(std::begin(accumulated_sum), -// std::end(accumulated_sum) - window_size); - -// size_t max_sum_idx = max_el - std::begin(accumulated_sum) + window_size; - -// double sum_w = 0; -// double prod_wx = 0; -// double wmc = 0; - -// for(int i = max_sum_idx - window_size; i < max_sum_idx; ++i) -// { -// prod_wx += column[i] * i; -// sum_w += column[i]; -// } - -// wmc = float(prod_wx) / float(sum_w); - -// result = img_height - wmc; - -// return result; -// #endif -// } - -// Pixels process_columns(Image &image) -// { -// Pixels result; -// result.counters = image.counters; - -// // std::cout << "here\n"; -// start_timer(process_columns); - -// for (size_t i = 0; i < image.width; i++) -// { -// // smooth_column(image.rotated_cw[i]); -// result.pixels[i] = process_column(image.rotated_cw[i]); -// // Algo genetic(image.rotated_cw[i]); - -// // image.pixels[i] = genetic.run().a; - -// // if (i == 0) { -// // std::cout << "pixel: " << image.pixels[i] << std::endl; -// // } -// } - -// stop_timer(process_columns); - -// return result; -// } - -QList pixelsToLines(const Pixels &rawProfile) -{ - const auto& pixels = rawProfile.pixels; - QList points(pixels.size()); - - for (int i = 0; i < pixels.size(); ++i) - { - points[i] = { qreal(i) - img_width / 2, pixels.at(i) }; - } - - // qDebug() << "mid points" << points.mid(points.count() - 3, 6); - - return pointsToLines(std::move(points)); -} - -QList pointsToLines(const QList &points) -{ - constexpr double maxDistanceFromLine { 3 }; - constexpr double minLineLength { 10 }; - - QList lineAnchors{0, int(points.count() - 1)}; - - auto vecLength = [](const QPointF& vector) { - return std::hypot(vector.x(), vector.y()); - }; - - auto distanceToLine = [vecLength](const QPointF& point, const QLineF& line) { - // const auto d1 = point - line.p1(); - // const auto d2 = line.p2() - line.p1(); - - // const auto norm = vecLength(d2); - // qDebug() << "norm" << norm; - - // if (norm <= std::numeric_limits::epsilon()) - // { - // qDebug() << "AAAAAAAAAAAAAAAAAA"; - // return vecLength(d1); - // } - - // const auto result = std::fabs(d1.x() * d2.y() - d1.y() * d2.x()) / norm; - - // if (!qFuzzyIsNull(result)) - // { - // qDebug() << "NOT NULL" << result << point << line; - // } - - // return result; - // transform to loocal coordinates system (0,0) - (lx, ly) - QPointF p1 = line.p1(); - QPointF p2 = line.p2(); - const auto& p = point; - qreal x = p.x() - p1.x(); - qreal y = p.y() - p1.y(); - qreal x2 = p2.x() - p1.x(); - qreal y2 = p2.y() - p1.y(); - - // if line is a point (nodes are the same) => - // just return distance between point and one line node - qreal norm = sqrt(x2*x2 + y2*y2); - if (norm <= std::numeric_limits::epsilon()) - return sqrt(x*x + y*y); - - // distance - // qDebug() << "dist" << fabs(x*y2 - y*x2) / norm << point << line; - return fabs(x * y2 - y * x2) / norm; - }; - - // for (const auto& p : points) - // { - // qDebug() << "\t" << p; - // } - - for (int i = 0; i < lineAnchors.count() - 1; ++i) - { - const auto &lineFirst = i; - const auto &lineLast = i + 1; - const auto& leftPointIdx = lineAnchors.at(lineFirst); - const auto& rightPointIdx = lineAnchors.at(lineLast); - - if (rightPointIdx - leftPointIdx < 2) - { - continue; - } - - const QLineF line { points.at(leftPointIdx), points.at(rightPointIdx) }; - - // std::cout << "max between " << leftPointIdx + 1 << " and " - // << rightPointIdx - 1 << std::endl; - - const auto farthest = std::max_element( - // std::execution::par_unseq, - points.cbegin() + leftPointIdx + 1, - points.cbegin() + rightPointIdx - 1, - [line, distanceToLine](const QPointF& a, const QPointF& b) { - return distanceToLine(a, line) < distanceToLine(b, line); - } - ); - - // std::cout << "done max" << std::endl; - // auto tmp = *farthest; - // std::cout << "done farthest" << std::endl; - - // qDebug() << "farthest point" << distanceToLine(*farthest, line); - // qDebug() << "farthest dist" << distanceToLine(*farthest, line); - // qDebug() << "that's from line" << line; - - if (distanceToLine(*farthest, line) > maxDistanceFromLine) - { - // std::cout << "try insert at " << i + 1 << ". lineAnchors.size is - // " - // << lineAnchors.size() - // << ". inserting this: " << farthest - points.cbegin() - // << std::endl; - lineAnchors.insert(i + 1, farthest - points.cbegin()); - // std::cout << "done insert" << std::endl; - --i; - // qDebug() << "I'm here" << i; - continue; - } - } - - struct LineAB - { - double a { std::numeric_limits::quiet_NaN() }; - double b { std::numeric_limits::quiet_NaN() }; - - LineAB(const QList& points) { - if (points.isEmpty()) - { - return; - } - - double sumX { 0 }; - double sumY { 0 }; - double sumXY { 0 }; - double sumXX { 0 }; - - // FIXME: validate - // for (const auto& point : points) - const int delta = points.count() * 0.15; - for (int i = delta; i < points.count() - delta; ++i) - { - const auto& point = points.at(i); - sumX += point.x(); - sumY += point.y(); - sumXY += point.x() * point.y(); - sumXX += point.x() * point.x(); - } - - // sumX /= points.count(); - // sumY /= points.count(); - // sumXY /= points.count(); - // sumXX /= points.count(); - - const int n = points.count() - delta * 2; - Q_ASSERT_X(n > 0, Q_FUNC_INFO, "n <= 0"); - - a = (n * sumXY - sumX * sumY) / - (n * sumXX - sumX * sumX); - b = (sumY - a * sumX) / n; - } - }; - - // auto pointsToLineAB = - - // qDebug() << "line anchors count is" << lineAnchors.count(); - - constexpr bool useLsd = true; - - QList result { lineAnchors.length() - 1 }; - - for (int i = 0; i < lineAnchors.count() - 1; ++i) - { - const auto& leftPointIdx = lineAnchors.at(i); - const auto& rightPointIdx = lineAnchors.at(i + 1); - - QPointF leftPoint = points.at(leftPointIdx); - QPointF rightPoint = points.at(rightPointIdx); - - LineAB lineAB(points.mid(leftPointIdx, rightPointIdx - leftPointIdx)); - - leftPoint.setY(lineAB.a * leftPoint.x() + lineAB.b); - rightPoint.setY(lineAB.a * rightPoint.x() + lineAB.b); - - if (useLsd) - result[i] = QLineF{ std::move(leftPoint), std::move(rightPoint) }; - else - result[i] = QLineF{ points.at(lineAnchors.at(i)), points.at(lineAnchors.at(i + 1)) }; - } - - if (useLsd) - { - for (int i = 0; i < result.count() - 1; ++i) - { - auto& lineA = result[i]; - auto& lineB = result[i + 1]; - - QPointF intersectionPoint {}; - - if (lineA.intersects(lineB, &intersectionPoint) != QLineF::NoIntersection) - { - lineA.setP2(intersectionPoint); - lineB.setP1(intersectionPoint); - } - } - } - - // qDebug() << result; - - return result; -} diff --git a/imagealgos.h b/imagealgos.h deleted file mode 100644 index e443e10..0000000 --- a/imagealgos.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include -#include - -#include "image.h" -#include "pixels.h" - -size_t pgm_save(std::shared_ptr img, - FILE *outfile = nullptr, - bool really_save = false); - -// void unpack_10bit(uint8_t const *src, Image const &image, uint16_t *dest); -// void unpack_16bit(uint8_t const *src, Image const &image, uint16_t *dest); - -// Pixels process_columns(Image & image); - -QList pixelsToLines(const Pixels& rawProfile); -QList pointsToLines(const QList& points); diff --git a/macro.h b/macro.h deleted file mode 100644 index 9bcc8e0..0000000 --- a/macro.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include - -#define start_timer(name) \ - std::chrono::steady_clock::time_point begin_##name = std::chrono::steady_clock::now(); - -#define stop_timer(name) \ - std::chrono::steady_clock::time_point end_##name = std::chrono::steady_clock::now(); \ - // std::cout << #name << " (us): " \ - // << std::chrono::duration_cast(end_##name - begin_##name) \ - // << std::endl; - -#define INIT_FIELD(name) m_##name(name) - -#define INNO_MAKER diff --git a/main.cpp b/main.cpp deleted file mode 100644 index 2166b3a..0000000 --- a/main.cpp +++ /dev/null @@ -1,795 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "LibCamera.h" -#include "calibration.h" -#include "camera/innomakerov9281.h" -#include "camera/ov9281.h" -#include "dumps.h" -#include "fuck_intel.h" -#include "genetic_algos.h" -#include "httpservice.h" -#include "imagealgos.h" -#include "laser.h" -#include "macro.h" -#include "pigpio.h" -#include "printerclient.h" -#include "profile.h" -#include "rotaryencoder.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define try_apply_config() \ - if (!applyConfig(config)) \ - { \ - camera->release(); \ - cm->stop(); \ -\ - return EXIT_FAILURE; \ - } - -ScanningModeFlags scanningModeFlags{ScanningModeFlags::None}; - -QElapsedTimer calibrationTimer; - -extern volatile int32_t positionSteps; - -requested_params_t requested_params; - -namespace { -std::shared_ptr img; -Pixels pixels; -std::vector calibrationPixels; -QMutex calibrationPixelsMutex; -} // namespace - -using namespace std::chrono_literals; - -// static std::shared_ptr camera; -// std::unique_ptr config; -// static std::map> mappedBuffers_; -// std::vector> requests; -libcamera::ControlList lastControls; - -namespace { -CalibrationTablePtr calibrationTableZ; -CalibrationTablePtr calibrationTableX; -} // namespace - -// static bool applyConfig( -// const std::unique_ptr& config -// ); -// static void onRequestCompleted(libcamera::Request* completed_request); -// static void printControls(); -// static QList filter(const QList& rawProfiles); - -auto printPixels = [](const auto& pixels) { - for (size_t i = (img_width - 10) / 2; - i < img_width - ((img_width - 10) / 2); - ++i) - { - std::cout << pixels[i] << " "; - } - std::cout << std::endl; -}; - -void onNewImage(std::shared_ptr image) -{ - // std::cout << __func__ << std::endl << std::flush; - - if (!image) - { - qDebug() << __func__ << "no image"; - return; - } - - ::img = image; -} - -void onNewPixels(std::shared_ptr pixels) -{ - // std::cout << __func__ << std::endl << std::flush; - - if (!pixels) - { - qDebug() << __func__ << "got null pixels"; - } - - if (!*pixels) - { - // qDebug() << __func__ << "got empty pixels"; - } - - for (size_t i = 640 - 5; i < 640 + 5; i++) - { - // std::cout << pixels->pixels[i] << " "; - } - - // std::cout << std::endl - - ::pixels = *pixels; -} - -bool initLaser(); - -int main(int argc, char* argv[]) -{ - QCoreApplication app(argc, argv); - - QList> initializers; - -#ifdef INNO_MAKER - if (false) - { - std::cout << std::boolalpha; - InnoMakerOV9281 innoMakerCam; - qDebug() << "init:" << innoMakerCam.init(); - qDebug() << "set exposure:" << innoMakerCam.setExposureTimeUs(3000000); - qDebug() << "set gain:" << innoMakerCam.setGain(2); - - innoMakerCam.startStream(); - QThread::sleep(3); - qDebug() << "should be stopped"; - // Image buf; - - // for (size_t i = 0; i < 1000; ++i) { - // if (!innoMakerCam.getImage(buf)) { - // break; - // } - - // buf.rotate(); - // auto pixels = buf.pixels(); - // } - } - // qDebug() << "ok"; - // exit(EXIT_SUCCESS); -#endif - - // if (false) - qDebug() << "size of raw profile" << sizeof(Pixels); - if (true) - { - // open binary calibration table - if (true) - { - initializers << QtConcurrent::run([&]() { - if (!openCalibrationTable( - "/home/user/dumps/binz.calibration_table", - ::calibrationTableZ)) - { - exit(EXIT_FAILURE); - } - }); - - initializers << QtConcurrent::run([&]() { - if (!openCalibrationTable( - "/home/user/dumps/binx.calibration_table", - ::calibrationTableX)) - { - exit(EXIT_FAILURE); - } - }); - } - - if (false) - { - // z - // if (!openCalibrationTable( - // "/home/user/dumps/binz.calibration_table", - // ::calibrationTableZ - // )) - // { - // exit(EXIT_FAILURE); - // } - - // if (!calibrationTableToImage(::calibrationTableZ) - // .save("/home/user/dumps/imageZ.png")) - // { - // qDebug() << "cannot save imageZ.png"; - // exit(EXIT_FAILURE); - // } - - // interpolate(::calibrationTableZ); - // exit(EXIT_SUCCESS); - - // calibrationTableToImage(::calibrationTableZ) - // .save("/home/user/dumps/imageZ_interpolated.png"); - - auto rawProfiles = openDump("/home/user/dumps/binx"); - qDebug() << "raw x-profiles count is" << rawProfiles.size(); - // qDebug() << "height" << calibrationColumnHeight; - - auto filteredRawProfiles = filter(std::move(rawProfiles)); - qDebug() << "filtered x-profiles count is" - << filteredRawProfiles.count(); - - ::calibrationTableX = calibrateX(std::move(filteredRawProfiles)); - - // for (size_t i = 9471; i < 9472; i++) { - // std::cout << "row #" << i << ": "; - - // for (size_t j = 0; j < 1280; ++j) { - // const auto& p = ::calibrationTableX->at(j).at(i); - // std::cout << p << ' '; - // } - - // std::cout << std::endl; - // } - - // x - // qDebug() << "open x table"; - // if (!openCalibrationTable("/home/user/dumps/binx.calibration_table", - // ::calibrationTableX)) { - // exit(EXIT_FAILURE); - // } - - // if (!calibrationTableToImage(::calibrationTableX) - // .save("/home/user/dumps/imageX.png")) { - // qDebug() << "cannot save imageX.png"; - // exit(EXIT_FAILURE); - // } - - // for (size_t i = 9471; i < 9472; i++) { - // std::cout << "row #" << i << ": "; - - // for (size_t j = 0; j < 1280; ++j) { - // const auto& p = ::calibrationTableX->at(j).at(i); - // std::cout << p << ' '; - // } - - // std::cout << std::endl; - // } - - // exit(EXIT_SUCCESS); - interpolate(::calibrationTableX); - - // calibrationTableToImage(::calibrationTableX) - // .save("/home/user/dumps/imageX_interpolated.png"); - } - - // load binary calibration dumps and calibrate - if (false) - { - if (true) - { - auto rawProfiles = openDump("/home/user/dumps/binz"); - // auto rawProfiles = openDump("/home/user/dumps/z"); - qDebug() << "raw z-profiles count is" << rawProfiles.size(); - // qDebug() << "height" << calibrationColumnHeight; - - auto filteredRawProfiles = filter(std::move(rawProfiles)); - qDebug() << "filtered z-profiles count is" - << filteredRawProfiles.count(); - - ::calibrationTableZ = calibrateZ(std::move(filteredRawProfiles), - requested_params.stepsPerMm); - - // bool ok = calibrationTableToImage(::calibrationTableZ) - // .save("/home/user/dumps/z/imageZ.png"); - - // if (!ok) - // { - // qDebug() << "cannot save imageZ.png"; - // exit(EXIT_FAILURE); - // } - - interpolate(::calibrationTableZ); - - if (!dump(::calibrationTableZ, - "/home/user/dumps/binz.calibration_table")) - { - qApp->exit(EXIT_FAILURE); - } - // calibrationTableToImage(::calibrationTableZ) - // .save("/home/user/dumps/z/imageZ_interpolated.png"); - // exit(EXIT_SUCCESS); - } - - qDebug() - << "--------------------------------------------------------"; - - if (true) - { - auto rawProfiles = openDump("/home/user/dumps/binx"); - qDebug() << "raw x-profiles count is" << rawProfiles.size(); - // qDebug() << "height" << calibrationColumnHeight; - - auto filteredRawProfiles = filter(std::move(rawProfiles)); - qDebug() << "filtered x-profiles count is" - << filteredRawProfiles.count(); - - ::calibrationTableX = calibrateX(std::move(filteredRawProfiles)); - - // bool ok = calibrationTableToImage(::calibrationTableX) - // .save("/home/user/dumps/z/imageX.png"); - - // if (!ok) - // { - // qDebug() << "cannot save imageX.png"; - // exit(EXIT_FAILURE); - // } - - interpolate(::calibrationTableX); - - if (!dump(::calibrationTableX, - "/home/user/dumps/binx.calibration_table")) - { - qApp->exit(EXIT_FAILURE); - } - - // calibrationTableToImage(::calibrationTableX) - // .save("/home/user/dumps/z/imageX_interpolated.png"); - } - } - } - - // exit(EXIT_SUCCESS); - - // if (!initLaser()) { - // return EXIT_FAILURE; - // } - - // PrinterClient printerClient; - - QElapsedTimer t; - t.start(); - - qDebug() << "msecs before encoder:" << t.elapsed(); - - RotaryEncoder encoder; - - qDebug() << "msecs before camera:" << t.elapsed(); - // FIXME: don't use one var for everything - int ret; -#ifndef INNO_MAKER - std::unique_ptr cm = - std::make_unique(); - cm->start(); -#endif - // const auto cameras = cm->cameras(); - // const auto cameras = OV9281::search(cm); - const auto cameras = InnoMakerOV9281::search(); - // const auto cameras = - - if (cameras.empty()) - { - std::cerr << "No cameras were identified on the system." << std::endl; -#ifndef INNO_MAKER - cm->stop(); -#endif - - return EXIT_FAILURE; - } - - auto camera = cameras.at(0); - -#ifndef INNO_MAKER - camera->printControls(); -#endif - - std::cout << "connect everything" << std::endl; - // camera->newPixels.connect(&onNewPixels); - // camera->newImage.connect(&onNewImage); - camera->newImageCallback = &onNewImage; - camera->newPixelsCallback = &onNewPixels; - - for (auto& i : initializers) - i.waitForFinished(); - - if (!camera->startStream()) - { -#ifndef INNO_MAKER - cm->stop(); -#endif - - return EXIT_FAILURE; - } - - QHttpServer qHttpServer; - qHttpServer.route("/v1/sensor/image", [&]() { - // std::cout << "http: image" << std::endl << std::flush; - // FILE *f = fopen("/tmp/img.pgm", "w"); - // static bool save = false; - pgm_save(::img); - // save = false; - std::lock_guard lg(pgm_image_mtx); - // qDebug() << "mutex locked"; - // qDebug() << "image saved to array"; - return QByteArray((const char*) pgm_image, pgm_image_size); - }); - qHttpServer.route("/v1/sensor/image2", [&]() { - // std::cout << "http: image2" << std::endl; - pgm_save(::img); - - std::lock_guard lg(pgm_image_mtx); - // qDebug() << "image2"; - return QByteArray((const char*) pgm_image, pgm_image_size); - }); - // qHttpServer.route("/v1/sensor/exposureTimeUs", [&]() { - // // std::lock_guard lg(pgm_image_mtx); - // return "123"; - // }); - qHttpServer.route("/v1/pixels", [&]() { - // std::cout << "http: pixels" << std::endl; - std::lock_guard lg(pgm_image_mtx); - - QJsonArray pixels; - - for (size_t i = 0; i < img_width; ++i) - { - // pixels << img_height - img.pixels[i]; - pixels << ::pixels.pixels[i]; - } - - QJsonObject json; - json["pixels"] = pixels; - json["encoderPosition"] = qint64{encoder.position()}; - // FIXME: get prom pixels struct - json["measurementCounter"] = qint64{img->counters.measurementCounter}; - json["timestampUs"] = qint64(img->counters.timestampUs); - - const auto lines = pixelsToLines(::pixels); - - // qDebug() << "lines count is " << lines.count(); - - QJsonArray jsonLines; - - for (const auto& l : lines) - { - jsonLines << QJsonArray{QJsonArray{l.p1().x(), l.p1().y()}, - QJsonArray{l.p2().x(), l.p2().y()}}; - } - - json["lines"] = jsonLines; - - return QHttpServerResponse(QJsonDocument(json).toJson()); - }); - - qHttpServer.route("/v1/profile", [&]() -> QHttpServerResponse { - // std::cout << "http: profile" << std::endl; - std::lock_guard lg(pgm_image_mtx); - - if (!::calibrationTableZ || !::calibrationTableX) - return QHttpServerResponse::StatusCode::ServiceUnavailable; - - const Profile profile(::pixels, - ::calibrationTableZ, - ::calibrationTableX); - - const QJsonObject json{{"profile", QJsonObject(profile)}}; - - return QHttpServerResponse(QJsonDocument(json).toJson()); - }); - - qHttpServer - .route("/v1/commands/resetEncoder", - [&](const QHttpServerRequest& request) -> QHttpServerResponse { - std::cout << "http: resetEncoder" << std::endl; - if (request.method() != QHttpServerRequest::Method::Post) - { - return QHttpServerResponse::StatusCode::NotFound; - } - - qDebug() << "reset encoder"; - - positionSteps = 0; - - return QHttpServerResponse::StatusCode::Ok; - }); - - qHttpServer - .route("/v1/commands/startCalibration", - [&](const QHttpServerRequest& request) -> QHttpServerResponse { - std::cout << "http: startCalibration" << std::endl; - if (request.method() != QHttpServerRequest::Method::Post) - { - return QHttpServerResponse::StatusCode::NotFound; - } - - qDebug() << "start calibration"; - - // TODO: use flags - scanningModeFlags = ScanningModeFlags::Calibration; - calibrationTimer.start(); - - return QHttpServerResponse::StatusCode::Ok; - }); - - qHttpServer - .route("/v1/commands/gCode", - [&](const QHttpServerRequest& request) -> QHttpServerResponse { - std::cout << "http: gCode" << std::endl; - if (request.method() != QHttpServerRequest::Method::Post) - { - return QHttpServerResponse::StatusCode::NotFound; - } - - const auto command = request.body(); - - qDebug() << "send gCode:" << command; - - // printerClient.sendCommand(command); - - return QHttpServerResponse::StatusCode::Ok; - }); - - // qHttpServer - // .route("/v1/commands/startCalibration", - // [&](const QHttpServerRequest& request) -> QHttpServerResponse { - // std::cout << "http: startCalibration" << std::endl; - // if (request.method() != QHttpServerRequest::Method::Post) - // { - // return QHttpServerResponse::StatusCode::NotFound; - // } - - // const auto command = request.body(); - - // qDebug() << "send gCode:" << command; - - // // printerClient.sendCommand(command); - - // return QHttpServerResponse::StatusCode::Ok; - // }); - - qHttpServer.route( - "/v1/sensor/params", - [&](const QHttpServerRequest& request) -> QHttpServerResponse { - // std::cout << "http: params" << std::endl; - switch (request.method()) - { - case QHttpServerRequest::Method::Get: { - std::lock_guard lg(pgm_image_mtx); - QJsonObject json; - - // const libcamera::ControlIdMap& ctrlIdMap = - // camera->controls().idmap(); - - // qDebug() << "readParams:" << lastControls.size(); - // qDebug() << request.method(); - - // for (const auto& [id, value] : lastControls) - // { - // const libcamera::ControlId* controlId = ctrlIdMap.at(id); - // auto name = QString::fromStdString(controlId->name()); - // const auto valueStr = - // QString::fromStdString(value.toString()); - // qDebug() - // << "\t param:" << controlId->id() << name << valueStr; - - // name[0] = name[0].toLower(); - // json[name] = valueStr; - // } - - // json[laserLevelKey] = requested_params.laserLevel; - - // qDebug() << "response body:" << json; - - // QHttpServerResponse - return QHttpServerResponse(QJsonDocument(json).toJson()); - } - - case QHttpServerRequest::Method::Post: { - // qDebug() << "request body:" << request.body(); - - auto json = QJsonDocument::fromJson(request.body()).object(); - - if (json.contains(exposureTimeKey)) - { - const int32_t value{json[exposureTimeKey].toInt()}; - - if (value == 0) - return QHttpServerResponse::StatusCode:: - RequestRangeNotSatisfiable; - - // qDebug() << "set new exposure time:" << value; - - // requested_params.exposureTime = value; - if (!camera->setExposureTimeUs(value)) - return QHttpServerResponse::StatusCode:: - RequestRangeNotSatisfiable; - } - - if (json.contains(gainKey)) - { - const int32_t value{json[gainKey].toInt()}; - - if (value == 0) - return QHttpServerResponse::StatusCode:: - RequestRangeNotSatisfiable; - - // qDebug() << "set gain:" << value; - - // requested_params.exposureTime = value; - if (!camera->setGain(value)) - return QHttpServerResponse::StatusCode:: - RequestRangeNotSatisfiable; - } - - if (json.contains(laserLevelKey)) - { - const int32_t value{json[laserLevelKey].toInt()}; - - // if (value == 0) - // { - // return QHttpServerResponse::StatusCode::NotFound; - // } - - // qDebug() << "set new laserLevel:" << value; - if (!camera->setLaserLevel(value)) - return QHttpServerResponse::StatusCode:: - RequestRangeNotSatisfiable; - - requested_params.laserLevel = value; - - // const QString laserLevelFile{ - // "/sys/class/pwm/pwmchip2/pwm1/duty_cycle"}; - // QFile f{laserLevelFile}; - - // if (!f.open(QFile::ReadWrite)) - // { - // qDebug() << "cannot open laser level file:" - // << f.errorString(); - // qDebug() << "file path is" << f.fileName(); - // return QHttpServerResponse::StatusCode::InternalServerError; - // } - - // QTextStream s{&f}; - - // s << value; - - // s >> requested_params.laserLevel; - - // qDebug() << "done with laser level"; - } - - return QHttpServerResponse(request.body()); - } - default: { - return QHttpServerResponse( - QByteArray("unsupported http method")); - } - } - }); - - qDebug() << "listen: " << qHttpServer.listen(QHostAddress::Any, 8081); - - QFuture future = QtConcurrent::run([]() { - Port port(8080); - Address addr(Ipv4::any(), port); - - HttpService httpService(addr); - - size_t threads_count = 1; - httpService.init(threads_count); - httpService.start(); - }); - - //////////////////////////////////////////////////////////////////////////// - std::clog << std::flush; - std::cerr << std::flush; - std::cout << "ok for now" << std::endl << std::flush; - - // camera->stop(); - // camera->release(); - // cm->stop(); - - auto result = app.exec(); - - future.cancel(); - future.waitForFinished(); - - // for (auto& [fd, mem] : mappedBuffers_) - // { - // munmap(mem.first, mem.second); - // } - - // FIXME: crash somewhere here. proper libcamera finishing needed - // requests.clear(); - // mappedBuffers_.clear(); - - // camera->stop(); - // config.reset(); - // allocator->free(stream); - // allocator.reset(); - // camera->release(); - // camera.reset(); -#ifndef INNO_MAKER - cm->stop(); -#endif - - return result; -} - -bool initLaser() -{ - const QLatin1String pwmChip{"pwmchip2"}; - const uint16_t pwmChannel{1}; - const QLatin1String pwmSystemRoot{"/sys/class/pwm"}; - const QString pwmChipRoot{pwmSystemRoot + "/" + pwmChip}; - - const QString pwmExportFile{pwmChipRoot + "/export"}; - - QFile f{pwmExportFile}; - - if (!f.open(QFile::WriteOnly)) - { - qWarning() << "cannot open" << f.fileName() << "for writing"; - qWarning() << "error:" << f.errorString(); - - return false; - } - - QTextStream s{&f}; - s << pwmChannel; - - const QString pwm{QLatin1String("pwm%1").arg(QString::number(pwmChannel))}; - const QString pwmRoot{pwmChipRoot + "/" + pwm}; - - const QString periodFilename{pwmRoot + "/period"}; - f.close(); - f.setFileName(periodFilename); - - if (!f.open(QFile::WriteOnly)) - { - qWarning() << "cannot open" << f.fileName() << "for writing"; - qWarning() << "error:" << f.errorString(); - - return false; - } - - const unsigned periodHz{50'000}; - - s << periodHz; - - const QString dutyCycleFilename{pwmRoot + "/duty_cycle"}; - f.close(); - f.setFileName(dutyCycleFilename); - - if (!f.open(QFile::WriteOnly)) - { - qWarning() << "cannot open" << f.fileName() << "for writing"; - qWarning() << "error:" << f.errorString(); - - return false; - } - - const unsigned dutyCycle{3'000}; - - s << dutyCycle; - - const QString enableFilename{pwmRoot + "/enable"}; - f.close(); - f.setFileName(enableFilename); - - if (!f.open(QFile::WriteOnly)) - { - qWarning() << "cannot open" << f.fileName() << "for writing"; - qWarning() << "error:" << f.errorString(); - - return false; - } - - const int enable{1}; - - s << enable; - - return true; -} diff --git a/printerclient.cpp b/printerclient.cpp deleted file mode 100644 index 4fed38f..0000000 --- a/printerclient.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "printerclient.h" - -#include - -#include -#include -#include -#include - -QString getFirstTtyUSB() -{ - auto ports = QSerialPortInfo::availablePorts(); - - std::remove_if(ports.begin(), ports.end(), [](const auto& port) { - return !port.portName().contains("ttyUSB"); - }); - - return ports.isEmpty() ? "" : ports.first().portName(); -} - -PrinterClient::PrinterClient(QObject* parent) - : QObject{parent} // , m_serialPort{new QSerialPort{"/dev/ttyUSB0", this}} - , m_serialPort{new QSerialPort{getFirstTtyUSB(), this}} -{ - if (!m_serialPort->setBaudRate(QSerialPort::Baud115200)) { - throw std::runtime_error( - "serial port: cannot set baud rate: " + - m_serialPort->errorString().toStdString()); - - return; - } - - if (!m_serialPort->open(QFile::ReadWrite)) { - throw std::runtime_error( - "cannot open serial port: " + - m_serialPort->errorString().toStdString()); - - return; - } - - qDebug() << "serial port baud rate:" << m_serialPort->baudRate(); - - qDebug() << "serial port data bits:" << m_serialPort->dataBits(); - qDebug() << "serial port parity:" << m_serialPort->parity(); - qDebug() << "serial port stop bits:" << m_serialPort->stopBits(); - - QObject::connect(m_serialPort, &QSerialPort::readyRead, - this, &PrinterClient::onReadyRead); - - QObject::connect(m_serialPort, &QSerialPort::errorOccurred, - this, &PrinterClient::onErrorOccured); - - // m_serialPort->write(QByteArray { "G91\n" }); - // m_serialPort->flush(); - // m_serialPort->write(QByteArray { "G1 Z10\n" }); - // m_serialPort->flush(); - sendCommand("G91"); - // sendCommand("G1 Z10"); - // sendCommand("G1 Z-10"); - - onErrorOccured(QSerialPort::SerialPortError::PermissionError); -} - -void PrinterClient::sendCommand(const QString command) -{ - const auto written = m_serialPort->write(command.toUtf8() + "\n"); - - qDebug() << QString("serialPort: send '%1': (written %2 bytes)") - .arg(command).arg(written); - - m_serialPort->flush(); -} - -void PrinterClient::onReadyRead() -{ - const auto data = m_serialPort->readAll(); - qDebug() << "serialPort: " << data; - - // emit newData(data); -} - -void PrinterClient::onErrorOccured(QSerialPort::SerialPortError error) -{ - qWarning() << "serial port error:" << m_serialPort->errorString() - << "-" << error; -} diff --git a/printerclient.h b/printerclient.h deleted file mode 100644 index d1266dd..0000000 --- a/printerclient.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include - -#include - -class QSerialPort; - -class PrinterClient : public QObject -{ - // Q_OBJECT - -public: - explicit PrinterClient(QObject *parent = nullptr); - // ~PrinterClient() override = default; - // ~PrinterClient - - // signals: - // void newData(const QString output); - -public: - void sendCommand(const QString command); - -private: - void onReadyRead(); - void onErrorOccured(QSerialPort::SerialPortError error); - -private: - QSerialPort* m_serialPort { nullptr }; -}; diff --git a/profile.cpp b/profile.cpp deleted file mode 100644 index 2c7b9a9..0000000 --- a/profile.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include "profile.h" - -#include - -#include -#include - -Profile::Profile( - const Pixels& pixels, - const CalibrationTablePtr calibrationTableZ, - const CalibrationTablePtr calibrationTableX) - : m_counters(pixels.counters) -{ - if (!calibrationTableZ || !calibrationTableX) - { - std::cerr << __func__ << ": got invalid calibration tables" - << std::endl; - - return; - } - - static bool done{false}; - - if (!done) { - for (size_t i = 9471; i < 9472; i++) { - std::cout << __func__ << ": row #" << i << ": "; - - for (size_t j = 0; j < 1280; ++j) { - const auto& x = calibrationTableX->at(j).at(i); - const auto& z = calibrationTableZ->at(j).at(i); - std::cout << "Profile: table: " << x << ' ' << z << std::endl; - } - - std::cout << std::endl; - } - } - - for (size_t i = 0; i < img_width; ++i) { - try { - const auto& pixel = pixels.pixels[i]; - const auto pixelDiscrete = pixel * discretesInRage / img_height; - if (Q_UNLIKELY(pixel >= sizeof(CalibrationColumn) / sizeof(float))) { - qWarning() << "got invalid calibration pixel at" << i << ":" - << pixel; - m_counters = {}; - m_pointsMm = {QPointF{std::nan(""), std::nan("")}}; - return; - } - - if (Q_UNLIKELY(pixel == sizeof(CalibrationColumn) - 1)) { - qDebug() << "Profile: got edge value"; - const auto& z = calibrationTableZ->at(i).at(pixelDiscrete); - - if (qFuzzyIsNull(z) || std::isnan(z)) { - m_pointsMm.at(i) = {std::nan(""), std::nan("")}; - continue; - } - - const auto& x = calibrationTableX->at(i).at(pixelDiscrete); - m_pointsMm.at(i) = {x, z}; - - continue; - } - - const auto& leftMmZ = calibrationTableZ->at(i).at( - size_t(pixelDiscrete)); - const auto& rightMmZ = calibrationTableZ->at(i).at( - size_t(pixelDiscrete) + 1); - - const auto& leftMmX = calibrationTableX->at(i).at( - size_t(pixelDiscrete)); - const auto& rightMmX = calibrationTableX->at(i).at( - size_t(pixelDiscrete) + 1); - - const auto& fract = pixelDiscrete - long(pixelDiscrete); - - const auto z = (leftMmZ * (1 - fract) + rightMmZ * fract); - - // TODO: use only NaN (or zero?) everywhere - // NOTE: QJsonValue converts NaN to zero - if (qFuzzyIsNull(z) || std::isnan(z)) { - // qDebug() << "got nan z for discrete" << pixelDiscrete << leftMmZ - // << rightMmZ; - m_pointsMm.at(i) = {std::nan(""), std::nan("")}; - continue; - } - - const auto x = (leftMmX * (1 - fract) + rightMmX * fract); - - m_pointsMm.at(i) = {x, z}; - - if (!done) { - qDebug() << "got these values\t" << pixels.pixels[i] - << pixelDiscrete << m_pointsMm[i] << leftMmZ - << rightMmZ << leftMmX << rightMmX; - } - } catch (const std::out_of_range& ex) { - qWarning() << "out of range exception:" << ex.what(); - qWarning() << "i:" << i; // << "pixels[i]" << pixels.pixels[i]; - m_counters = {}; - m_pointsMm = {QPointF{std::nan(""), std::nan("")}}; - - return; - } catch (const std::exception& ex) { - qWarning() << "exception:" << ex.what(); - qWarning() << "i:" << i << "pixels[i]" << pixels.pixels[i]; - m_counters = {}; - m_pointsMm = {QPointF{std::nan(""), std::nan("")}}; - - return; - } catch (...) { - qWarning() << "unknown exception"; - m_counters = {}; - m_pointsMm = {QPointF{std::nan(""), std::nan("")}}; - return; - } - } - - done = true; - - // static bool printOnce = [&]() -> bool { - // for (size_t i = 0; i < m_pointsMm.size(); ++i) { - // qDebug() << '\t' << pixels.pixels[i] << m_pointsMm[i]; - // } - - // return true; - // }(); -} - -const Counters& Profile::counters() const -{ - return m_counters; -} - -const Profile::PointsMm& Profile::pointsMm() const -{ - return m_pointsMm; -} - -Profile::operator const QJsonObject() const -{ - QJsonObject counters{ - {"timestampUs", qint64(m_counters.timestampUs)}, - {"measurementCounter", qint64(m_counters.measurementCounter)}, - {"encoderPosition", qint64(m_counters.encoderPosition)}, - }; - - QJsonArray jsonPoints; - - for (const auto& p : m_pointsMm) { - jsonPoints << QJsonArray{p.x(), p.y()}; - } - - return {{"counters", counters}, {"pointsMm", jsonPoints}}; -} diff --git a/profile.h b/profile.h deleted file mode 100644 index 0e2f839..0000000 --- a/profile.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include -#include - -#include "calibration.h" -#include "pixels.h" - -class Profile -{ -public: - using PointsMm = std::array; - -public: - // TODO: make private/protected - explicit Profile(const Pixels& pixels, - const CalibrationTablePtr calibrationTableZ, - const CalibrationTablePtr calibrationTableX); - -public: - const Counters& counters() const; - const PointsMm& pointsMm() const; - - operator const QJsonObject() const; - -private: - Counters m_counters{}; - PointsMm m_pointsMm{QPointF{std::nan(""), std::nan("")}}; -}; diff --git a/rotaryencoder.cpp b/rotaryencoder.cpp deleted file mode 100644 index 62ca33d..0000000 --- a/rotaryencoder.cpp +++ /dev/null @@ -1,125 +0,0 @@ -#include "rotaryencoder.h" - -#include -#include -#include -#include - -#include -#include - -#include - -// got from https://gist.github.com/ast/a19813fce9d34c7240091db11b8190dd -// https://gist.github.com/ast -// Inspired by Paul Stoffregen's excellent Arduino library Encoder: -// https://github.com/PaulStoffregen/Encoder - -constexpr int gpioA = 17; -constexpr int gpioB = 27; -// constexpr int gpioA = 0; -// constexpr int gpioB = 2; -// constexpr int gpioA = 11; -// constexpr int gpioB = 13; - -const std::vector encoderTable = { - 0, 1, -1, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, -1, 1, 0 -}; - -volatile int32_t positionSteps; -volatile uint8_t state; - -void pin_isr(void) { - uint8_t p1val = digitalRead(gpioA); - uint8_t p2val = digitalRead(gpioB); - uint8_t s = state & 3; - - if (p1val) s |= 4; - if (p2val) s |= 8; - - state = (s >> 2); - - switch (s) { - case 1: case 7: case 8: case 14: - positionSteps = positionSteps + 1; - return; - case 2: case 4: case 11: case 13: - positionSteps = positionSteps - 1; - return; - case 3: case 12: - positionSteps = positionSteps + 2; - return; - case 6: case 9: - positionSteps = positionSteps - 2; - return; - } -} - -RotaryEncoder::RotaryEncoder() -{ - if (!m_self) { - m_self = this; - } else { - qWarning() << "normally there should be only one instance of RotaryEncoder"; - } - - QElapsedTimer t; - t.start(); - - if (wiringPiSetupGpio()) { - perror("wiringPiSetupGpio"); - exit(EXIT_FAILURE); - } - - qDebug() << "msecs to setup wiringPi:" << t.elapsed(); - - if ( wiringPiISR (gpioA, INT_EDGE_BOTH, &pin_isr) < 0 ) { - perror("wiringPiISR"); - exit(EXIT_FAILURE); - } - - qDebug() << "msecs to register interruption A:" << t.elapsed(); - - if ( wiringPiISR (gpioB, INT_EDGE_BOTH, &pin_isr) < 0 ) { - perror("wiringPiISR"); - exit(EXIT_FAILURE); - } - - qDebug() << "msecs to register interruption B:" << t.elapsed(); - - // pinMode (gpioA, INPUT) ; - // pinMode (gpioB, INPUT) ; - // pullUpDnControl(gpioA, PUD_UP); - // pullUpDnControl(gpioB, PUD_UP); - - qDebug() << "encoder is ok"; - - // // Show position every second - // while ( 1 ) { - // constexpr double stepsPerMm = 200; - // const double positionMm = ::positionSteps / stepsPerMm; - // qDebug() << ::positionSteps - // << '-' - // << QString::number(positionMm, 'f', 3) - // << "(mm)"; - // // printf( "%ld\n", ::position); - // usleep( 100 * 1000 ); // wait 1 second - // } -} - -RotaryEncoder::~RotaryEncoder() -{ - if (m_self == this) { - m_self = nullptr; - } -} - -RotaryEncoder *RotaryEncoder::instance() -{ - return m_self; -} - -int32_t RotaryEncoder::position() const -{ - return ::positionSteps; -} diff --git a/rotaryencoder.h b/rotaryencoder.h deleted file mode 100644 index c0c39d8..0000000 --- a/rotaryencoder.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include - -// TODO: delete singleton functionality -class RotaryEncoder final -{ -public: - RotaryEncoder(); - ~RotaryEncoder(); - -public: - static RotaryEncoder* instance(); - -public: - int32_t position() const; - -private: - static inline RotaryEncoder* m_self { nullptr }; -}; diff --git a/src/LibCamera.cpp b/src/LibCamera.cpp new file mode 100644 index 0000000..1e68228 --- /dev/null +++ b/src/LibCamera.cpp @@ -0,0 +1,267 @@ +// #include "LibCamera.h" + +// using namespace std::placeholders; + +// int LibCamera::initCamera() { +// int ret; +// cm = std::make_unique(); +// ret = cm->start(); +// if (ret){ +// std::cout << "Failed to start camera manager: " +// << ret << std::endl; +// return ret; +// } +// cameraId = cm->cameras()[0]->id(); +// camera_ = cm->get(cameraId); +// if (!camera_) { +// std::cerr << "Camera " << cameraId << " not found" << std::endl; +// return 1; +// } + +// if (camera_->acquire()) { +// std::cerr << "Failed to acquire camera " << cameraId +// << std::endl; +// return 1; +// } +// camera_acquired_ = true; +// return 0; +// } + +// char * LibCamera::getCameraId(){ +// return cameraId.data(); +// } + +// void LibCamera::configureStill(int width, int height, PixelFormat format, int buffercount, int rotation) { +// printf("Configuring still capture...\n"); +// config_ = camera_->generateConfiguration({ StreamRole::StillCapture }); +// if (width && height) { +// libcamera::Size size(width, height); +// config_->at(0).size = size; +// } +// config_->at(0).pixelFormat = format; +// if (buffercount) +// config_->at(0).bufferCount = buffercount; +// Transform transform = Transform::Identity; +// bool ok; +// Transform rot = transformFromRotation(rotation, &ok); +// if (!ok) +// throw std::runtime_error("illegal rotation value, Please use 0 or 180"); +// transform = rot * transform; +// if (!!(transform & Transform::Transpose)) +// throw std::runtime_error("transforms requiring transpose not supported"); +// // FIXME: update +// // config_->transform = transform; + +// CameraConfiguration::Status validation = config_->validate(); +// if (validation == CameraConfiguration::Invalid) +// throw std::runtime_error("failed to valid stream configurations"); +// else if (validation == CameraConfiguration::Adjusted) +// std::cout << "Stream configuration adjusted" << std::endl; + +// printf("Still capture setup complete\n"); +// } + +// int LibCamera::startCamera() { +// int ret; +// ret = camera_->configure(config_.get()); +// if (ret < 0) { +// std::cout << "Failed to configure camera" << std::endl; +// return ret; +// } + +// camera_->requestCompleted.connect(this, &LibCamera::requestComplete); + +// allocator_ = std::make_unique(camera_); + +// return startCapture(); +// } + +// int LibCamera::startCapture() { +// int ret; +// unsigned int nbuffers = UINT_MAX; +// for (StreamConfiguration &cfg : *config_) { +// ret = allocator_->allocate(cfg.stream()); +// if (ret < 0) { +// std::cerr << "Can't allocate buffers" << std::endl; +// return -ENOMEM; +// } + +// unsigned int allocated = allocator_->buffers(cfg.stream()).size(); +// nbuffers = std::min(nbuffers, allocated); +// } + +// for (unsigned int i = 0; i < nbuffers; i++) { +// std::unique_ptr request = camera_->createRequest(); +// if (!request) { +// std::cerr << "Can't create request" << std::endl; +// return -ENOMEM; +// } + +// for (StreamConfiguration &cfg : *config_) { +// Stream *stream = cfg.stream(); +// const std::vector> &buffers = +// allocator_->buffers(stream); +// const std::unique_ptr &buffer = buffers[i]; + +// ret = request->addBuffer(stream, buffer.get()); +// if (ret < 0) { +// std::cerr << "Can't set buffer for request" +// << std::endl; +// return ret; +// } +// for (const FrameBuffer::Plane &plane : buffer->planes()) { +// void *memory = mmap(NULL, plane.length, PROT_READ, MAP_SHARED, +// plane.fd.get(), 0); +// mappedBuffers_[plane.fd.get()] = +// std::make_pair(memory, plane.length); +// } +// } + +// requests_.push_back(std::move(request)); +// } + +// ret = camera_->start(&this->controls_); +// // ret = camera_->start(); +// if (ret) { +// std::cout << "Failed to start capture" << std::endl; +// return ret; +// } +// controls_.clear(); +// camera_started_ = true; +// for (std::unique_ptr &request : requests_) { +// ret = queueRequest(request.get()); +// if (ret < 0) { +// std::cerr << "Can't queue request" << std::endl; +// camera_->stop(); +// return ret; +// } +// } +// viewfinder_stream_ = config_->at(0).stream(); +// return 0; +// } + +// void LibCamera::StreamDimensions(Stream const *stream, uint32_t *w, uint32_t *h, uint32_t *stride) const +// { +// StreamConfiguration const &cfg = stream->configuration(); +// if (w) +// *w = cfg.size.width; +// if (h) +// *h = cfg.size.height; +// if (stride) +// *stride = cfg.stride; +// } + +// Stream *LibCamera::VideoStream(uint32_t *w, uint32_t *h, uint32_t *stride) const +// { +// StreamDimensions(viewfinder_stream_, w, h, stride); +// return viewfinder_stream_; +// } + +// int LibCamera::queueRequest(Request *request) { +// std::lock_guard stop_lock(camera_stop_mutex_); +// if (!camera_started_) +// return -1; +// { +// std::lock_guard lock(control_mutex_); +// request->controls() = std::move(controls_); +// } +// return camera_->queueRequest(request); +// } + +// void LibCamera::requestComplete(Request *request) { +// if (request->status() == Request::RequestCancelled) +// return; +// processRequest(request); +// } + +// void LibCamera::processRequest(Request *request) { +// requestQueue.push(request); +// } + +// void LibCamera::returnFrameBuffer(LibcameraOutData frameData) { +// uint64_t request = frameData.request; +// Request * req = (Request *)request; +// req->reuse(Request::ReuseBuffers); +// queueRequest(req); +// } + +// bool LibCamera::readFrame(LibcameraOutData *frameData){ +// std::lock_guard lock(free_requests_mutex_); +// // int w, h, stride; +// if (!requestQueue.empty()){ +// Request *request = this->requestQueue.front(); + +// const Request::BufferMap &buffers = request->buffers(); +// for (auto it = buffers.begin(); it != buffers.end(); ++it) { +// FrameBuffer *buffer = it->second; +// for (unsigned int i = 0; i < buffer->planes().size(); ++i) { +// const FrameBuffer::Plane &plane = buffer->planes()[i]; +// const FrameMetadata::Plane &meta = buffer->metadata().planes()[i]; + +// void *data = mappedBuffers_[plane.fd.get()].first; +// int length = std::min(meta.bytesused, plane.length); + +// frameData->size = length; +// frameData->imageData = (uint8_t *)data; +// } +// } +// this->requestQueue.pop(); +// frameData->request = (uint64_t)request; +// return true; +// } else { +// Request *request = nullptr; +// frameData->request = (uint64_t)request; +// return false; +// } +// } + +// void LibCamera::set(ControlList controls){ +// std::lock_guard lock(control_mutex_); +// this->controls_ = std::move(controls); +// } + +// int LibCamera::resetCamera(int width, int height, PixelFormat format, int buffercount, int rotation) { +// stopCamera(); +// configureStill(width, height, format, buffercount, rotation); +// return startCamera(); +// } + +// void LibCamera::stopCamera() { +// if (camera_){ +// { +// std::lock_guard lock(camera_stop_mutex_); +// if (camera_started_){ +// if (camera_->stop()) +// throw std::runtime_error("failed to stop camera"); +// camera_started_ = false; +// } +// } +// camera_->requestCompleted.disconnect(this, &LibCamera::requestComplete); +// } +// while (!requestQueue.empty()) +// requestQueue.pop(); + +// for (auto &iter : mappedBuffers_) +// { +// std::pair pair_ = iter.second; +// munmap(std::get<0>(pair_), std::get<1>(pair_)); +// } + +// mappedBuffers_.clear(); + +// requests_.clear(); + +// allocator_.reset(); + +// controls_.clear(); +// } + +// void LibCamera::closeCamera(){ +// if (camera_acquired_) +// camera_->release(); +// camera_acquired_ = false; + +// camera_.reset(); + +// cm.reset(); +// } diff --git a/src/LibCamera.h b/src/LibCamera.h new file mode 100644 index 0000000..89cf835 --- /dev/null +++ b/src/LibCamera.h @@ -0,0 +1,91 @@ +#pragma once + +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include +// #include + +#ifdef signals +#error ("don't include this file after any qt files") +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// using namespace libcamera; + +// typedef struct { +// uint8_t *imageData; +// uint32_t size; +// uint64_t request; +// } LibcameraOutData; + +// class LibCamera { +// public: +// LibCamera(){}; +// ~LibCamera(){}; + +// int initCamera(); +// void configureStill(int width, int height, PixelFormat format, int buffercount, int rotation); +// int startCamera(); +// int resetCamera(int width, int height, PixelFormat format, int buffercount, int rotation); +// bool readFrame(LibcameraOutData *frameData); +// void returnFrameBuffer(LibcameraOutData frameData); + +// void set(ControlList controls); +// void stopCamera(); +// void closeCamera(); + +// Stream *VideoStream(uint32_t *w, uint32_t *h, uint32_t *stride) const; +// char * getCameraId(); + +// private: +// int startCapture(); +// int queueRequest(Request *request); +// void requestComplete(Request *request); +// void processRequest(Request *request); + +// void StreamDimensions(Stream const *stream, uint32_t *w, uint32_t *h, uint32_t *stride) const; + +// unsigned int cameraIndex_; +// uint64_t last_; +// std::unique_ptr cm; +// std::shared_ptr camera_; +// bool camera_acquired_ = false; +// bool camera_started_ = false; +// std::unique_ptr config_; +// std::unique_ptr allocator_; +// std::vector> requests_; +// // std::map stream_; +// std::map> mappedBuffers_; + +// std::queue requestQueue; + +// ControlList controls_; +// std::mutex control_mutex_; +// std::mutex camera_stop_mutex_; +// std::mutex free_requests_mutex_; + +// Stream *viewfinder_stream_ = nullptr; +// std::string cameraId; +// }; diff --git a/src/camera/icamera.h b/src/camera/icamera.h index 3fccc4b..11919bb 100644 --- a/src/camera/icamera.h +++ b/src/camera/icamera.h @@ -35,12 +35,14 @@ public: virtual bool setExposureTimeUs(int value) = 0; virtual bool setGain(int value) = 0; virtual bool setLaserLevel(int value) = 0; + virtual bool setSomething(int value) = 0; public: libcamera::Signal> newPixels; libcamera::Signal> newImage; std::function)> newPixelsCallback; - std::function)> newImageCallback; + // std::function)> newImageCallback; + std::function newImageCallback; public: virtual bool startStream() = 0; diff --git a/src/camera/innomakerov9281.cpp b/src/camera/innomakerov9281.cpp index b7c5cd4..79ae0b4 100644 --- a/src/camera/innomakerov9281.cpp +++ b/src/camera/innomakerov9281.cpp @@ -10,6 +10,9 @@ #include #include +#include + +#include #include "constants.h" // #include "rotaryencoder.h" @@ -23,10 +26,13 @@ #define DBG(fmt, args...) LOGD("%s:%d, " fmt, __FUNCTION__, __LINE__, ##args); +extern uint64_t dq_elapsed_ns; +extern uint64_t get_elapsed_ns; extern uint64_t sum_elapsed_ns; extern uint64_t corr_elapsed_ns; extern uint64_t max_elapsed_ns; extern uint64_t value_elapsed_ns; +extern uint64_t rot_elapsed_ns; // constexpr char videoDevice[] = "/dev/video0"; @@ -37,6 +43,9 @@ InnoMakerOV9281::~InnoMakerOV9281() m_streamThread.request_stop(); m_streamThread.join(); + m_someThread.request_stop(); + m_someThread.join(); + int ret{-1}; for (int i = 0; i < BUFFER_COUNT; ++i) @@ -74,43 +83,84 @@ std::vector > InnoMakerOV9281::search() if (!cam->init()) return {}; - if (!cam->setExposureTimeUs(3000)) + if (!cam->setExposureTimeUs(68000)) + return {}; + + if (!cam->setLaserLevel(1)) return {}; - if (!cam->setGain(3000)) + if (!cam->setGain(2)) return {}; + if (!cam->setSomething(0)) { + return {}; + } + + // m_someThread = std::jthread{[=](std::stop_token stopToken) { + // std::cout << "InnoMakerOV9281: start stream" << std::endl; + // sleep(5); + + // static int i = 0; + // while (!stopToken.stop_requested()) { + // cam->setSomething(i); + // i -= 1; + // } + // }}; + return {cam}; } bool InnoMakerOV9281::startStream() { + int buffer_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + auto ret = ioctl(m_cam_fd, VIDIOC_STREAMON, &buffer_type); + + if (ret != 0) { + DBG("ioctl(VIDIOC_STREAMON) failed %d(%s)", errno, strerror(errno)); + return false; + } + m_streamThread = std::jthread{[&](std::stop_token stopToken) { std::cout << "InnoMakerOV9281: start stream" << std::endl; + // Image image; + std::shared_ptr imageCopy = std::make_shared(); while (!stopToken.stop_requested()) { - std::shared_ptr image = std::make_shared(); - getImage(*image); -// FIXME: backup emit value -#ifdef emit -#undef emit - // std::cout << "emit new image" << std::endl << std::flush; - // newImage.emit(image); - if (newImageCallback) - { + size_t imageIndex{}; + + // if (!getImage(image)) + if (!getImage(imageIndex)) + break; + + auto image = m_images[imageIndex]; + + // // FIXME: backup emit value + // #ifdef emit + // #undef emit + // // std::cout << "emit new image" << std::endl << std::flush; + // // newImage.emit(image); + if (newImageCallback) { + // memcpy(imageCopy.get(), &image, sizeof(Image)); + // newImageCallback(imageCopy); newImageCallback(image); } - // newPixels.emit(pixels); - image->rotate(); - if (newPixelsCallback) - { - auto pixels = image->pixels(); + // continue; + // // // newPixels.emit(pixels); + // auto &img = *image; + // image->rotate(); + // img.rotate(); + image.rotate(); + // image.rotate(); + if (newPixelsCallback) { + // auto pixels = image->pixels(); + auto pixels = image.pixels(); newPixelsCallback(pixels); } + continue; } -#define emit -#endif + // #define emit + // #endif std::cout << "InnoMakerOV9281: stream interruption requested" << std::endl; }}; @@ -170,6 +220,13 @@ bool InnoMakerOV9281::setLaserLevel(int value) return setCamParam(V4L2_CID_FLASH_TIMEOUT, value); } +bool InnoMakerOV9281::setSomething(int value) +{ + std::cout << __func__ << ": " << value << std::endl << std::flush; + + return setCamParam(V4L2_CID_FLASH_INTENSITY, value); +} + bool InnoMakerOV9281::setCamParam(unsigned int v4l2controlId, int value) { v4l2_control ctl{v4l2controlId, value}; @@ -182,6 +239,7 @@ bool InnoMakerOV9281::setCamParam(unsigned int v4l2controlId, int value) "cannot set cam param: id - %d, error - '%s'\n", v4l2controlId, strerror(errno)); + fflush(stderr); return false; } @@ -195,6 +253,8 @@ bool InnoMakerOV9281::setCamParam(unsigned int v4l2controlId, int value) v4l2controlId, strerror(errno)); + fflush(stderr); + return false; } @@ -277,11 +337,8 @@ bool InnoMakerOV9281::initCam() request.memory = V4L2_MEMORY_MMAP; ret = ioctl(m_cam_fd, VIDIOC_REQBUFS, &request); - if (ret < 0) - { - fprintf(stderr, - "cannot set cam request buffers: ioctl error - '%s'\n", - strerror(errno)); + if (ret < 0) { + fprintf(stderr, "cannot set cam request buffers: ioctl error - '%s'\n", strerror(errno)); return false; } @@ -311,14 +368,9 @@ bool InnoMakerOV9281::initCam() DBG("buffer.length: %d", buffer.length); DBG("buffer.m.offset: %d", buffer.m.offset); - video_buffer_ptr[i] = (uint8_t *) mmap(NULL, - buffer.length, - PROT_READ | PROT_WRITE, - MAP_SHARED, - m_cam_fd, - buffer.m.offset); - if (video_buffer_ptr[i] == MAP_FAILED) - { + video_buffer_ptr[i] = (uint8_t *) + mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, m_cam_fd, buffer.m.offset); + if (video_buffer_ptr[i] == MAP_FAILED) { DBG("mmap() failed %d(%s)", errno, strerror(errno)); return false; } @@ -334,20 +386,23 @@ bool InnoMakerOV9281::initCam() } } - int buffer_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ret = ioctl(m_cam_fd, VIDIOC_STREAMON, &buffer_type); - if (ret != 0) - { - DBG("ioctl(VIDIOC_STREAMON) failed %d(%s)", errno, strerror(errno)); - return false; - } + // int buffer_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + // ret = ioctl(m_cam_fd, VIDIOC_STREAMON, &buffer_type); + // if (ret != 0) + // { + // DBG("ioctl(VIDIOC_STREAMON) failed %d(%s)", errno, strerror(errno)); + // return false; + // } DBG("cam init done."); return true; } -bool InnoMakerOV9281::getImage(Image &image) +// Image &InnoMakerOV9281::getImage() +// bool InnoMakerOV9281::getImage(Image &image) +bool InnoMakerOV9281::getImage(size_t &imageIndex) +// TODO: get Image from video_buffer_ptr { static struct timeval curr, prev; static uint16_t counter = 0; @@ -359,17 +414,25 @@ bool InnoMakerOV9281::getImage(Image &image) if (elapsedTime > 1000.) { - fprintf(stderr, "fps: %d, sec: %d\n", counter, curr.tv_sec); fprintf(stderr, - "sum: %d,\tcorr: %d,\tval: %d\n", + "fps: %d\tsec: %d\tdq: %d\tget: %d\trot: %d\tsum: %d,\tcorr: %d,\tval: %d\n", + counter, + curr.tv_sec % 1000, + dq_elapsed_ns / 1000 / counter, + get_elapsed_ns / 1000 / counter, + rot_elapsed_ns / 1000 / counter, sum_elapsed_ns / 1000 / counter, corr_elapsed_ns / 1000 / counter, // max_elapsed_ns / 1000 / counter, value_elapsed_ns / 1000 / counter); + + dq_elapsed_ns = 0; + get_elapsed_ns = 0; sum_elapsed_ns = 0; corr_elapsed_ns = 0; max_elapsed_ns = 0; value_elapsed_ns = 0; + rot_elapsed_ns = 0; counter = 0; prev = curr; @@ -383,10 +446,14 @@ bool InnoMakerOV9281::getImage(Image &image) buffer.memory = V4L2_MEMORY_MMAP; buffer.index = BUFFER_COUNT; - ret = ioctl(m_cam_fd, VIDIOC_DQBUF, &buffer); - - if (ret != 0) { + QElapsedTimer t; + t.start(); + ret = ioctl(m_cam_fd, VIDIOC_DQBUF, &buffer); + dq_elapsed_ns += t.nsecsElapsed(); + } + + if (ret != 0) { DBG("ioctl(VIDIOC_DQBUF) failed %d(%s)", errno, strerror(errno)); return false; } @@ -397,19 +464,34 @@ bool InnoMakerOV9281::getImage(Image &image) return false; } + // auto &image = video_buffer_ptr[buffer.index]; + imageIndex = buffer.index; + auto &image = m_images[buffer.index]; image.height = img_height; image.width = img_width; // TODO: fill // image.counters.encoderPosition = RotaryEncoder::instance()->position(); image.counters.measurementCounter = buffer.sequence; - image.counters.timestampUs = buffer.timestamp.tv_sec * 1000 * 1000 + - buffer.timestamp.tv_usec; - memcpy(image.data, video_buffer_ptr[buffer.index], img_size); + image.counters.timestampUs = buffer.timestamp.tv_sec * 1000 * 1000 + buffer.timestamp.tv_usec; + + { + QElapsedTimer t; + t.start(); + memcpy(&image.data, video_buffer_ptr[buffer.index], img_size); + // std::cout << (void *) video_buffer_ptr[buffer.index] << std::endl; + get_elapsed_ns += t.nsecsElapsed(); + } + + static bool done{false}; + + if (!done) { + // memcpy(image.data.data(), video_buffer_ptr[buffer.index], img_size); + done = true; + } ret = ioctl(m_cam_fd, VIDIOC_QBUF, &buffer); - if (ret != 0) - { + if (ret != 0) { DBG("ioctl(VIDIOC_QBUF) failed %d(%s)", errno, strerror(errno)); return false; } diff --git a/src/camera/innomakerov9281.h b/src/camera/innomakerov9281.h index ccfe1ac..7f2d206 100644 --- a/src/camera/innomakerov9281.h +++ b/src/camera/innomakerov9281.h @@ -33,7 +33,11 @@ public: bool setLaserLevel(int value) override; - bool getImage(Image &image); + bool setSomething(int value) override; + + // bool getImage(Image &image); + bool getImage(size_t &image); + // Image &getImage(); public: libcamera::Signal> newPixels; @@ -48,7 +52,12 @@ private: private: int m_cam_fd{-1}; static constexpr uint8_t BUFFER_COUNT{3}; + // #ifdef USER_PTR + Image m_images[BUFFER_COUNT]; + // #else uint8_t *video_buffer_ptr[BUFFER_COUNT]; + // #endif // buffer_t m_buf; std::jthread m_streamThread; + static inline std::jthread m_someThread; }; diff --git a/src/camera/ov9281.cpp b/src/camera/ov9281.cpp index 1a324e4..5b6936d 100644 --- a/src/camera/ov9281.cpp +++ b/src/camera/ov9281.cpp @@ -259,14 +259,14 @@ std::vector> OV9281::search( { auto id = camera->id(); auto c = manager->get(id); - auto ov9281 = std::shared_ptr(new OV9281(c)); + // auto ov9281 = std::shared_ptr(new OV9281(c)); - if (!ov9281->init()) - { - continue; - } + // if (!ov9281->init()) + // { + // continue; + // } - result.push_back(ov9281); + // result.push_back(ov9281); } return result; diff --git a/src/constants.h b/src/constants.h index 4f37fe5..53e6063 100644 --- a/src/constants.h +++ b/src/constants.h @@ -10,7 +10,7 @@ constexpr size_t img_width = 1280; constexpr size_t img_height = 800; constexpr size_t img_size = img_width * img_height; -constexpr uint32_t patternSize = 16; +constexpr uint32_t patternSize = 10; constexpr float hardcodedZRangeMm{175.f}; constexpr size_t calibrationTableHeight{0x4000}; // 16384 diff --git a/src/fuck_intel.h b/src/fuck_intel.h new file mode 100644 index 0000000..6d9f9d5 --- /dev/null +++ b/src/fuck_intel.h @@ -0,0 +1,12 @@ +#pragma once + +// include this file before including any qt files if you have emit conflict +// with tbb + +#ifdef emit +#undef emit +#include +#define emit +#else +#include +#endif diff --git a/src/genetic_algos.cpp b/src/genetic_algos.cpp new file mode 100644 index 0000000..1962768 --- /dev/null +++ b/src/genetic_algos.cpp @@ -0,0 +1 @@ +#include "genetic_algos.h" diff --git a/src/genetic_algos.h b/src/genetic_algos.h new file mode 100644 index 0000000..8dea732 --- /dev/null +++ b/src/genetic_algos.h @@ -0,0 +1,174 @@ +#pragma once + +// #define _USE_MATH_DEFINES +#include +#include +#include +#include +// TODO: + +/* + +a, sigma + +*/ + +// #define POP_SIZE 30 +// #define NUM_IT 25 +// #define COL_SIZE 16 + +// using Column = double; + +static std::random_device random_device; +static std::mt19937 gen(random_device()); +static std::uniform_real_distribution<> dis01(0., 1.); +static std::uniform_real_distribution<> disDelta2(-2., 2.); + +template +struct Item { + double a, sigma; // E, sigma = sqrt(D) + T* column; + + double W; + + Item() {} + + Item(double a_, double s, T * column_) : a(a_), sigma(s), column(column_) { + update(); + } + + double gauss(double t) { + return std::exp(std::pow((t - a) / sigma, + 2) / + 2) / + std::sqrt(2 * std::numbers::pi) / + sigma; + } + + double F() { // objective function + double s = 0; + int x0 = std::floor(.5 + a - 3 * sigma); + double x1 = std::round(a + 3 * sigma); + + for (int j = x0; j <= x1; j++) + s += std::pow(gauss(j) - column[j], 2); + + return s; + } + + void update() { + W = F(); + } + + // action + + Item move() { + double a1 = a + disDelta2(gen); + double sigma1 = sigma * (1 + disDelta2(gen)); // a: ~ +- 2 pixel + + return Item(a1, sigma1, column); + } + + // a = q * a1 + (1 - q) * a2, sigma = q * s1 + (1 - q) * s2 + Item crossover(const Item& other) { + double q = dis01(gen); + double a_ = q * a + (1 - q) * other.a; + double sigma_ = q * sigma + (1 - q) * other.sigma; + + return Item(a_, sigma_, column); + } +}; + +template +struct Algo { + T * column; + using I = Item; + std::vector population; + std::uniform_real_distribution disA { 0., double(1.) }; + + double targetW; + + Algo(T * column_): column(column_) { + init(); + } + + I getNewItem() { + double a = rand() % (COL_SIZE * 1000) / 1000.; + double sigma = rand() % (COL_SIZE * 100) / 1000.; + + return I(a, sigma, column); + } + + void init() { + for (size_t i = 0; i < POP_SIZE; i++) { + population.push_back(getNewItem()); + } + } + + bool stopCondition() { + // return population[0].W <= targetW; + return population[0].W <= maxW; + } + + I run() { + for (int it = 0; it < NUM_IT; it++) { + work(); + + // if (stopCondition()) + // break; + } + + return population[0]; + } + + void work() { + move(); + crossover(); + addNew(); + + sort(); + + remove(); + } + + void sort() { + // sort vector ASC + std::sort(population.begin(), population.end(), + [](const auto & a, const auto & b) + { + return a.W < b.W; + }); + } + + void remove() { + population.erase(population.begin() + POP_SIZE, population.end()); + } + + void move() { + for (I& item : population) { + population.push_back(item.move()); + } + } + + void addNew() { + for (int i = 0; i < 5; i++) { + population.push_back(getNewItem()); + } + } + + void crossover() { + for (int i1 = 0; i1 < 4; i1++) { + + for (int i2 = i1 + 1; i2 < 4; i2++) { + I& x1 = population[i1]; + I& x2 = population[i2]; + // a = q * a1 + (1 - q) * a2, sigma = q * s1 + (1 - q) * s2 + population.push_back(x1.crossover(x2)); + } + } + } +}; diff --git a/src/httpservice.cpp b/src/httpservice.cpp new file mode 100644 index 0000000..7fa72fb --- /dev/null +++ b/src/httpservice.cpp @@ -0,0 +1 @@ +#include "httpservice.h" diff --git a/src/httpservice.h b/src/httpservice.h new file mode 100644 index 0000000..a6c3f76 --- /dev/null +++ b/src/httpservice.h @@ -0,0 +1,203 @@ +#pragma once + +#include +#include +#include + +#include + +#include "imagealgos.h" + +using namespace Pistache; + +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; + +class HttpService +{ +public: + HttpService(Address addr) + : httpEndpoint(std::make_shared(addr)) + , desc("Banking API", "0.1") + { } + + void init(size_t thr = 2) + { + auto opts = Http::Endpoint::options() + .threads(static_cast(thr)); + httpEndpoint->init(opts); + createDescription(); + } + + void start() + { + router.initFromDescription(desc); + + Rest::Swagger swagger(desc); + swagger + .uiPath("/doc") + .uiDirectory("/home/user/swagger-ui/dist") + .apiPath("/banker-api.json") + .serializer(&Rest::Serializer::rapidJson) + .install(router); + + httpEndpoint->setHandler(router.handler()); + httpEndpoint->serve(); + } + +private: + void createDescription() + { + desc + .info() + .license("Apache", "http://www.apache.org/licenses/LICENSE-2.0"); + + auto backendErrorResponse = desc.response(Http::Code::Internal_Server_Error, "An error occured with the backend"); + + desc + .schemes(Rest::Scheme::Http) + .basePath("/v1") + .produces(MIME(Application, Json)) + .consumes(MIME(Application, Json)); + + // desc + // .route(desc.get("/ready")) + // .bind(&Generic::handleReady) + // .response(Http::Code::Ok, "Response to the /ready call") + // .hide(); + + auto versionPath = desc.path("/v1"); + + auto sensorPath = versionPath.path("/sensor"); + + sensorPath + .route(desc.get("/image")) + .bind(&HttpService::image, this) + .produces(MIME(Image, Png)) + .response(Http::Code::Ok, "Image from sensor"); + + // tmp + sensorPath + .route(desc.get("/image2")) + .bind(&HttpService::image, this) + .produces(MIME(Image, Png)) + .response(Http::Code::Ok, "Image from sensor"); + + sensorPath + .route(desc.get("/params"), "Retrive sensor parameters") + .bind(&HttpService::get_sensorParams, this) + .produces(MIME(Application, Plain)) + .response(Http::Code::Ok, "Parameter value") + .response(backendErrorResponse);; + + sensorPath + .route(desc.get("/:param"), "Retrive sensor parameter") + .bind(&HttpService::get_sensorParam, this) + .produces(MIME(Application, Json)) + .parameter("param", "The name of the parameter to retrieve") + .response(Http::Code::Ok, "Parameter value") + .response(backendErrorResponse);; + + sensorPath + .route(desc.post("/:param"), "Set sensor parameter") + .bind(&HttpService::set_sensorParam, this) + .produces(MIME(Application, Plain)) + .consumes(MIME(Application, Plain)) + .response(Http::Code::Ok, "Setting parameter result"); + + + auto accountsPath = versionPath.path("/accounts"); + + accountsPath + .route(desc.get("/all")) + .bind(&HttpService::retrieveAllAccounts, this) + .produces(MIME(Application, Json), MIME(Application, Xml)) + .response(Http::Code::Ok, "The list of all account"); + + accountsPath + .route(desc.get("/:name"), "Retrieve an account") + .bind(&HttpService::retrieveAccount, this) + .produces(MIME(Application, Json)) + .parameter("name", "The name of the account to retrieve") + .response(Http::Code::Ok, "The requested account") + .response(backendErrorResponse); + + accountsPath + .route(desc.post("/:name"), "Create an account") + .bind(&HttpService::createAccount, this) + .produces(MIME(Application, Json)) + .consumes(MIME(Application, Json)) + .parameter("name", "The name of the account to create") + .response(Http::Code::Ok, "The initial state of the account") + .response(backendErrorResponse); + auto accountPath = accountsPath.path("/:name"); + accountPath.parameter("name", "The name of the account to operate on"); + + accountPath + .route(desc.post("/budget"), "Add budget to the account") + .bind(&HttpService::creditAccount, this) + .produces(MIME(Application, Json)) + .response(Http::Code::Ok, "Budget has been added to the account") + .response(backendErrorResponse); + } + + void retrieveAllAccounts(const Rest::Request&, Http::ResponseWriter response) + { + response.send(Http::Code::Ok, + "No Account", + { Http::Mime::Type::Text, Http::Mime::Subtype::Plain} ); + } + + void retrieveAccount(const Rest::Request&, Http::ResponseWriter response) + { + response.send(Http::Code::Ok, "The bank is closed, come back later"); + } + + void createAccount(const Rest::Request&, Http::ResponseWriter response) + { + response.send(Http::Code::Ok, "The bank is closed, come back later"); + } + + void creditAccount(const Rest::Request&, Http::ResponseWriter response) + { + response.send(Http::Code::Ok, "The bank is closed, come back later"); + } + + void image(const Rest::Request&, Http::ResponseWriter response) + { + // FIXME: image should be valid + std::lock_guard lg(pgm_image_mtx); + // char data[pgm_image_size]; + // memcpy(data, pgm_image, pgm_image_size); + std::cout << "send bytes: " << pgm_image_size << std::endl; + + auto res = response.send(Http::Code::Ok, + (const char*)pgm_image, pgm_image_size, + Http::Mime::MediaType { "image/pgm" }); + // { Http::Mime::Type::Image, Http::Mime::Subtype::Png }); + + res.then([](ssize_t bytes) + { std::cout << bytes << " bytes have been sent\n"; }, + Async::NoExcept); + } + + void get_sensorParam(const Rest::Request&, Http::ResponseWriter response) + { + response.send(Http::Code::Ok, std::to_string(123)); + } + + void get_sensorParams(const Rest::Request&, Http::ResponseWriter response) + { + response.send(Http::Code::Ok, std::to_string(123)); + } + + void set_sensorParam(const Rest::Request&, Http::ResponseWriter response) + { + response.send(Http::Code::Ok, std::to_string(123)); + } + + std::shared_ptr httpEndpoint; + Rest::Description desc; + Rest::Router router; +}; diff --git a/src/image.cpp b/src/image.cpp index e4d51c1..befcb2b 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -9,12 +9,17 @@ #include "macro.h" #include "pixels.h" +uint64_t dq_elapsed_ns = 0; +uint64_t get_elapsed_ns = 0; uint64_t sum_elapsed_ns = 0; uint64_t corr_elapsed_ns = 0; uint64_t max_elapsed_ns = 0; uint64_t value_elapsed_ns = 0; +uint64_t rot_elapsed_ns = 0; -float process_column(const uint8_t (&column)[]) +// float process_column(const uint8_t (&column)[]) +// float process_column(const Image::row_t &column) +float process_column(const Image::column_t &column) { start_timer(process_column); QElapsedTimer t; @@ -23,39 +28,45 @@ float process_column(const uint8_t (&column)[]) float result = std::numeric_limits::quiet_NaN(); constexpr uint32_t signalThreshold = 900; // = SKO * sqrt(patternSize) - static constexpr uint32_t patternOffset = patternSize - - ((patternSize % 2 == 1) ? 1 : 0); - const uint32_t correlationSize = img_height - patternSize + - ((patternSize % 2 == 1) ? 1 : 0); + static constexpr uint32_t patternOffset = patternSize - ((patternSize % 2 == 1) ? 1 : 0); + const uint32_t correlationSize = img_height - patternSize + ((patternSize % 2 == 1) ? 1 : 0); + // constexpr uint32_t correlationSize = img_height - patternSize; uint32_t correlation[img_height]; uint32_t integralSum[img_height]; - uint32_t maxSum = signalThreshold * 50; + uint32_t maxTripleSum = signalThreshold * 50; uint32_t x1 = 0; int32_t y1 = 0; int32_t y2 = 0; memset(correlation, 0, img_height * sizeof(correlation[0])); + // memset(correlation, 0, patternSize / 2); integralSum[0] = 0; for (uint32_t i = 1; i < img_height; ++i) { integralSum[i] = column[i] + integralSum[i - 1]; } + sum_elapsed_ns += t.nsecsElapsed(); t.restart(); + // pixel * for (uint32_t i = 0; i < correlationSize; ++i) correlation[i + patternSize / 2] = column[i + patternSize / 2] * (integralSum[i + patternOffset] - integralSum[i]); + // * (integralSum[i + patternSize] - integralSum[i]); corr_elapsed_ns += t.nsecsElapsed(); t.restart(); for (uint32_t i = 3; i < img_height - 2; ++i) { + // p - pixel, n - neighbour + // P - pixel used in sum, N - neighbour used in sum + // [N P N] const auto sum = correlation[i - 1] + correlation[i] + correlation[i + 1]; - if (sum > maxSum) - { + if (sum > maxTripleSum) { + // [N N n p] - [P N] const int32_t rioux0 = int32_t(correlation[i - 2 - 1] + correlation[i - 1 - 1]) - int32_t(correlation[i + 1 - 1] + @@ -63,6 +74,7 @@ float process_column(const uint8_t (&column)[]) if (rioux0 < 0) { + // [N N p] - [p N N] const int32_t rioux1 = int32_t(correlation[i - 2] + correlation[i - 1]) - int32_t(correlation[i + 1] + @@ -73,7 +85,7 @@ float process_column(const uint8_t (&column)[]) x1 = i - 1; y1 = rioux0; y2 = rioux1; - maxSum = sum; + maxTripleSum = sum; } } } @@ -88,23 +100,40 @@ float process_column(const uint8_t (&column)[]) return result; } +// uint8_t &Image::dataAt(size_t row, size_t col) +// { +// const auto index = img_width * row + col; +// return *(data + index); +// } + void Image::rotate() { - start_timer(rotate); + QElapsedTimer t; + t.start(); + // start_timer(rotate); using namespace std; + // Image::data_t &d = *data; #pragma omp parallel #pragma omp for - for (size_t i = 0; i < img_height; ++i) - { - for (size_t j = 0; j < img_width; ++j) - { - rotated_cw[j][i] = data[img_height - i][j]; + for (size_t i = 0; i < img_height; ++i) { + for (size_t j = 0; j < img_width; ++j) { + // for (size_t j = 0; j < img_width; j += 4) { + rotated_cw[j][i] = data[img_height - i - 1][j]; + // rotated_cw[j][i] = d[img_height - i - 1][j]; + + // rotated_cw[j + 0][i] = data[img_height - i - 1][j + 0]; + // rotated_cw[j + 1][i] = data[img_height - i - 1][j + 1]; + // rotated_cw[j + 2][i] = data[img_height - i - 1][j + 2]; + // rotated_cw[j + 3][i] = data[img_height - i - 1][j + 3]; + + // rotated_cw[j][i] = dataAt(img_height - i - 1, j); } } - stop_timer(rotate); + // stop_timer(rotate); + rot_elapsed_ns += t.nsecsElapsed(); } std::shared_ptr Image::pixels() const @@ -113,6 +142,11 @@ std::shared_ptr Image::pixels() const result->counters = counters; start_timer(process_columns); + // std::transform(std::execution::par_unseq, + // rotated_cw.cbegin(), + // rotated_cw.cend(), + // result->pixels.begin(), + // [](const auto &column) -> float { return process_column(column); }); #pragma omp chunk #pragma omp parallel for @@ -139,15 +173,21 @@ void Image::copyFromData(const void *src, size_t size) switch (pixelFormat) { case libcamera::formats::R8: { // std::cout << "R8" << std::endl; - memcpy(data, src, size); + // memcpy(data, src, size); + // memcpy(data->data(), src, size); + memcpy(&data, src, size); + // data = (uint8_t *) src; break; } case libcamera::formats::R16: { // std::cout << "R16" << std::endl; + // data_t &d = *data; #pragma omp parallel #pragma omp for for (size_t i = 0; i < img_size; i++) { data[i / img_width][i % img_width] = (((uint16_t *) src)[i] & 0xff00) >> 8; + // d[i / img_width][i % img_width] = (((uint16_t *) src)[i] & 0xff00) >> 8; + // dataAt(i / img_width, i % img_width) = (((uint16_t *) src)[i] & 0xff00) >> 8; } break; } diff --git a/src/image.h b/src/image.h index 934aa40..b4e8a58 100644 --- a/src/image.h +++ b/src/image.h @@ -3,6 +3,8 @@ #include "constants.h" #include "typedefs.h" +#define USER_PTR + class Pixels; // TODO: template @@ -10,8 +12,20 @@ struct Image { int width{0}; int height{0}; - uint8_t data[img_height][img_width] = {{0}}; - uint8_t rotated_cw[img_width][img_height] = {{0}}; + // uint8_t data[img_height][img_width] = {{0}}; + + using row_t = std::array; + using data_t = std::array; + using rotated_row_t = std::array; + using column_t = rotated_row_t; + using rotated_data_t = std::array; + // data_t d; + data_t data; + // data_t *data; + // uint8_t *data = {nullptr}; + // uint8_t &dataAt(size_t row, size_t col); + // uint8_t rotated_cw[img_width][img_height] = {{0}}; + rotated_data_t rotated_cw; // size_t dataSize{0}; // unsigned int stride{0}; libcamera::PixelFormat pixelFormat{0}; diff --git a/src/imagealgos.cpp b/src/imagealgos.cpp new file mode 100644 index 0000000..2257244 --- /dev/null +++ b/src/imagealgos.cpp @@ -0,0 +1,465 @@ +#include "imagealgos.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "macro.h" + +uint8_t pgm_image[64 + img_width * img_height * sizeof(uint8_t)]; +size_t pgm_image_size = 0; +std::mutex pgm_image_mtx; + +template +T median3(const T &a, const T &b, const T &c) +{ + using namespace std; + return max(min(a, b), min(max(a, b), c)); +} + +// size_t pgm_save(std::shared_ptr img, FILE *outfile, bool really_save) +size_t pgm_save(Image *img, FILE *outfile, bool really_save) +{ + std::lock_guard lg(pgm_image_mtx); + + size_t n{0}; + + n += sprintf((char*)pgm_image, "P5\n%d %d\n%d\n", + img->width, img->height, 0xFF); + + memcpy(pgm_image + n, img->data.data(), sizeof(img->data)); + // memcpy(pgm_image + n, img->data->data(), sizeof(img->data)); + n += sizeof(img->data); + pgm_image_size = n; + + if (really_save) + { + if (outfile) { + fwrite(pgm_image, 1, pgm_image_size, outfile); + fflush(outfile); + + } else { + std::cerr << __func__ << ": output filename is nullptr, cannot save" + << std::endl; + } + } + + // std::cout << "written pgm image" << std::endl; + return n; +} + +// void unpack_10bit(uint8_t const *src, Image const &image, uint16_t *dest) +// { +// unsigned int w_align = image.width & ~3; +// for (unsigned int y = 0; y < image.height; y++, src += image.stride) +// { +// uint8_t const *ptr = src; +// unsigned int x; +// for (x = 0; x < w_align; x += 4, ptr += 5) +// { +// *dest++ = (ptr[0] << 2) | ((ptr[4] >> 0) & 3); +// *dest++ = (ptr[1] << 2) | ((ptr[4] >> 2) & 3); +// *dest++ = (ptr[2] << 2) | ((ptr[4] >> 4) & 3); +// *dest++ = (ptr[3] << 2) | ((ptr[4] >> 6) & 3); +// } +// for (; x < image.width; x++) +// *dest++ = (ptr[x & 3] << 2) | ((ptr[4] >> ((x & 3) << 1)) & 3); +// } +// } + +// void unpack_16bit(uint8_t const *src, Image const &image, uint16_t *dest) +// { +// start_timer(unpack_16bit); +// /* Assume the pixels in memory are already in native byte order */ +// unsigned int w = image.width; + +// for (unsigned int y = 0; y < image.height; y++) +// { +// memcpy(dest, src, 2 * w); +// dest += w; +// src += image.stride; +// } +// stop_timer(unpack_16bit); +// } + +template +constexpr size_t mysize(T (&)[N]) { return N; } + +// void smooth_column(uint8_t (&column)[]) +// { +// for (size_t i = 1; i < img_height - 1; ++i) { +// column[i] = median3(column[i - 1], column[i], column[i + 1]); +// } +// } + +// float process_column(uint8_t (&column)[]) +// { +// std::cout << "aaaaaaaaaaa\n"; +// start_timer(process_column); + +// float result = std::numeric_limits::quiet_NaN(); + +// constexpr uint32_t signalThreshold = 900; // = SKO * sqrt(patternSize) +// static constexpr uint32_t patternOffset = patternSize - ((patternSize % 2 == 1) ? 1 : 0); +// const uint32_t correlationSize = img_height - patternSize + ((patternSize % 2 == 1) ? 1 : 0); +// uint32_t correlation[img_height]; +// uint32_t integralSum[img_height]; +// uint32_t maxSum = signalThreshold * 50; +// uint32_t x1 = 0; +// int32_t y1 = 0; +// int32_t y2 = 0; + +// memset(correlation, 0, img_height * sizeof(correlation[0])); +// integralSum[0] = 0; + +// for (uint32_t i = 1; i < img_height; ++i) { +// // if (column[i + 0] < 100) { column[i + 0] = 0; } integralSum[i + 0] = column[i + 0] / 256 + integralSum[i + 0 - 1]; +// // if (column[i + 1] < 100) { column[i + 1] = 0; } integralSum[i + 1] = column[i + 1] / 256 + integralSum[i + 1 - 1]; +// // if (column[i + 2] < 100) { column[i + 2] = 0; } integralSum[i + 2] = column[i + 2] / 256 + integralSum[i + 2 - 1]; +// // if (column[i + 3] < 100) { column[i + 3] = 0; } integralSum[i + 3] = column[i + 3] / 256 + integralSum[i + 3 - 1]; +// // if (column[i + 4] < 100) { column[i + 4] = 0; } integralSum[i + 4] = column[i + 4] / 256 + integralSum[i + 4 - 1]; +// // if (column[i + 5] < 100) { column[i + 5] = 0; } integralSum[i + 5] = column[i + 5] / 256 + integralSum[i + 5 - 1]; +// // if (column[i + 6] < 100) { column[i + 6] = 0; } integralSum[i + 6] = column[i + 6] / 256 + integralSum[i + 6 - 1]; +// // if (column[i + 7] < 100) { column[i + 7] = 0; } integralSum[i + 7] = column[i + 7] / 256 + integralSum[i + 7 - 1]; +// // if (column[i + 8] < 100) { column[i + 8] = 0; } integralSum[i + 8] = column[i + 8] / 256 + integralSum[i + 8 - 1]; +// // if (column[i + 9] < 100) { column[i + 9] = 0; } integralSum[i + 9] = column[i + 9] / 256 + integralSum[i + 9 - 1]; +// // if (column[i + 10] < 100) { column[i + 10] = 0; } integralSum[i + 10] = column[i + 10] / 256 + integralSum[i + 10 - 1]; +// // if (column[i + 11] < 100) { column[i + 11] = 0; } integralSum[i + 11] = column[i + 11] / 256 + integralSum[i + 11 - 1]; +// // if (column[i + 12] < 100) { column[i + 12] = 0; } integralSum[i + 12] = column[i + 12] / 256 + integralSum[i + 12 - 1]; +// // if (column[i + 13] < 100) { column[i + 13] = 0; } integralSum[i + 13] = column[i + 13] / 256 + integralSum[i + 13 - 1]; +// // if (column[i + 14] < 100) { column[i + 14] = 0; } integralSum[i + 14] = column[i + 14] / 256 + integralSum[i + 14 - 1]; +// // if (column[i + 15] < 100) { column[i + 15] = 0; } integralSum[i + 15] = column[i + 15] / 256 + integralSum[i + 15 - 1]; + +// if (column[i] < 100) { +// column[i] = 0; +// } +// integralSum[i] = column[i] + integralSum[i - 1]; +// } + +// // maxSum = 0 ; +// // size_t maxIdx { 0 }; + +// // for (size_t i = 0; i < img_height - patternSize; ++i) { +// // const auto sum = integralSum[i + patternSize] - integralSum[i]; +// // if (sum > maxSum) { +// // maxSum = sum; +// // maxIdx = i; +// // } +// // } + +// // Algo genetic(column + maxIdx); +// // // std::cout << "maxIdx " << maxIdx << std::endl; + +// // // return maxIdx + genetic.run().a; +// // return 500; +// // return img_height - maxIdx - genetic.run().a; + +// for (uint32_t i = 0; i < correlationSize; ++i) +// correlation[i + patternSize / 2] = column[i + patternSize / 2] / 256 +// * (integralSum[i + patternOffset] - integralSum[i]); + +// for (uint32_t i = 3; i < img_height - 2; ++i) { +// const auto sum = correlation[i - 1] + correlation[i] + correlation[i + 1]; + +// if (sum > maxSum) { +// const int32_t rioux0 = int32_t(correlation[i - 2 - 1] + correlation[i - 1 - 1]) +// - int32_t(correlation[i + 1 - 1] + correlation[i + 2 - 1]); + +// if (rioux0 < 0) { +// const int32_t rioux1 = int32_t(correlation[i - 2] + correlation[i - 1]) +// - int32_t(correlation[i + 1] + correlation[i + 2]); + +// if (rioux1 >= 0) { +// x1 = i - 1; +// y1 = rioux0; +// y2 = rioux1; +// maxSum = sum; +// } +// } +// } +// } + +// result = (y2 != y1) ? (float(x1) - (float(y1) / (y2 - y1))) +// : std::numeric_limits::quiet_NaN(); + +// static bool result_done = false; +// if (!result_done) { +// std::cout << "result " << result << std::endl; +// result_done = true; +// } +// // std::cout << "result is '" << result << "'\n"; + +// // stop_timer(process_column); + +// return result; + +// // center of mass +// #if 0 +// auto max_el = std::max_element(std::begin(accumulated_sum), +// std::end(accumulated_sum) - window_size); + +// size_t max_sum_idx = max_el - std::begin(accumulated_sum) + window_size; + +// double sum_w = 0; +// double prod_wx = 0; +// double wmc = 0; + +// for(int i = max_sum_idx - window_size; i < max_sum_idx; ++i) +// { +// prod_wx += column[i] * i; +// sum_w += column[i]; +// } + +// wmc = float(prod_wx) / float(sum_w); + +// result = img_height - wmc; + +// return result; +// #endif +// } + +// Pixels process_columns(Image &image) +// { +// Pixels result; +// result.counters = image.counters; + +// // std::cout << "here\n"; +// start_timer(process_columns); + +// for (size_t i = 0; i < image.width; i++) +// { +// // smooth_column(image.rotated_cw[i]); +// result.pixels[i] = process_column(image.rotated_cw[i]); +// // Algo genetic(image.rotated_cw[i]); + +// // image.pixels[i] = genetic.run().a; + +// // if (i == 0) { +// // std::cout << "pixel: " << image.pixels[i] << std::endl; +// // } +// } + +// stop_timer(process_columns); + +// return result; +// } + +QList pixelsToLines(const Pixels &rawProfile) +{ + const auto& pixels = rawProfile.pixels; + QList points(pixels.size()); + + for (int i = 0; i < pixels.size(); ++i) + { + points[i] = { qreal(i) - img_width / 2, pixels.at(i) }; + } + + // qDebug() << "mid points" << points.mid(points.count() - 3, 6); + + return pointsToLines(std::move(points)); +} + +QList pointsToLines(const QList &points) +{ + constexpr double maxDistanceFromLine { 3 }; + constexpr double minLineLength { 10 }; + + QList lineAnchors{0, int(points.count() - 1)}; + + auto vecLength = [](const QPointF& vector) { + return std::hypot(vector.x(), vector.y()); + }; + + auto distanceToLine = [vecLength](const QPointF& point, const QLineF& line) { + // const auto d1 = point - line.p1(); + // const auto d2 = line.p2() - line.p1(); + + // const auto norm = vecLength(d2); + // qDebug() << "norm" << norm; + + // if (norm <= std::numeric_limits::epsilon()) + // { + // qDebug() << "AAAAAAAAAAAAAAAAAA"; + // return vecLength(d1); + // } + + // const auto result = std::fabs(d1.x() * d2.y() - d1.y() * d2.x()) / norm; + + // if (!qFuzzyIsNull(result)) + // { + // qDebug() << "NOT NULL" << result << point << line; + // } + + // return result; + // transform to loocal coordinates system (0,0) - (lx, ly) + QPointF p1 = line.p1(); + QPointF p2 = line.p2(); + const auto& p = point; + qreal x = p.x() - p1.x(); + qreal y = p.y() - p1.y(); + qreal x2 = p2.x() - p1.x(); + qreal y2 = p2.y() - p1.y(); + + // if line is a point (nodes are the same) => + // just return distance between point and one line node + qreal norm = sqrt(x2*x2 + y2*y2); + if (norm <= std::numeric_limits::epsilon()) + return sqrt(x*x + y*y); + + // distance + // qDebug() << "dist" << fabs(x*y2 - y*x2) / norm << point << line; + return fabs(x * y2 - y * x2) / norm; + }; + + // for (const auto& p : points) + // { + // qDebug() << "\t" << p; + // } + + for (int i = 0; i < lineAnchors.count() - 1; ++i) + { + const auto &lineFirst = i; + const auto &lineLast = i + 1; + const auto& leftPointIdx = lineAnchors.at(lineFirst); + const auto& rightPointIdx = lineAnchors.at(lineLast); + + if (rightPointIdx - leftPointIdx < 2) + { + continue; + } + + const QLineF line { points.at(leftPointIdx), points.at(rightPointIdx) }; + + // std::cout << "max between " << leftPointIdx + 1 << " and " + // << rightPointIdx - 1 << std::endl; + + const auto farthest = std::max_element( + // std::execution::par_unseq, + points.cbegin() + leftPointIdx + 1, + points.cbegin() + rightPointIdx - 1, + [line, distanceToLine](const QPointF& a, const QPointF& b) { + return distanceToLine(a, line) < distanceToLine(b, line); + } + ); + + // std::cout << "done max" << std::endl; + // auto tmp = *farthest; + // std::cout << "done farthest" << std::endl; + + // qDebug() << "farthest point" << distanceToLine(*farthest, line); + // qDebug() << "farthest dist" << distanceToLine(*farthest, line); + // qDebug() << "that's from line" << line; + + if (distanceToLine(*farthest, line) > maxDistanceFromLine) + { + // std::cout << "try insert at " << i + 1 << ". lineAnchors.size is + // " + // << lineAnchors.size() + // << ". inserting this: " << farthest - points.cbegin() + // << std::endl; + lineAnchors.insert(i + 1, farthest - points.cbegin()); + // std::cout << "done insert" << std::endl; + --i; + // qDebug() << "I'm here" << i; + continue; + } + } + + struct LineAB + { + double a { std::numeric_limits::quiet_NaN() }; + double b { std::numeric_limits::quiet_NaN() }; + + LineAB(const QList& points) { + if (points.isEmpty()) + { + return; + } + + double sumX { 0 }; + double sumY { 0 }; + double sumXY { 0 }; + double sumXX { 0 }; + + // FIXME: validate + // for (const auto& point : points) + const int delta = points.count() * 0.15; + for (int i = delta; i < points.count() - delta; ++i) + { + const auto& point = points.at(i); + sumX += point.x(); + sumY += point.y(); + sumXY += point.x() * point.y(); + sumXX += point.x() * point.x(); + } + + // sumX /= points.count(); + // sumY /= points.count(); + // sumXY /= points.count(); + // sumXX /= points.count(); + + const int n = points.count() - delta * 2; + Q_ASSERT_X(n > 0, Q_FUNC_INFO, "n <= 0"); + + a = (n * sumXY - sumX * sumY) / + (n * sumXX - sumX * sumX); + b = (sumY - a * sumX) / n; + } + }; + + // auto pointsToLineAB = + + // qDebug() << "line anchors count is" << lineAnchors.count(); + + constexpr bool useLsd = true; + + QList result { lineAnchors.length() - 1 }; + + for (int i = 0; i < lineAnchors.count() - 1; ++i) + { + const auto& leftPointIdx = lineAnchors.at(i); + const auto& rightPointIdx = lineAnchors.at(i + 1); + + QPointF leftPoint = points.at(leftPointIdx); + QPointF rightPoint = points.at(rightPointIdx); + + LineAB lineAB(points.mid(leftPointIdx, rightPointIdx - leftPointIdx)); + + leftPoint.setY(lineAB.a * leftPoint.x() + lineAB.b); + rightPoint.setY(lineAB.a * rightPoint.x() + lineAB.b); + + if (useLsd) + result[i] = QLineF{ std::move(leftPoint), std::move(rightPoint) }; + else + result[i] = QLineF{ points.at(lineAnchors.at(i)), points.at(lineAnchors.at(i + 1)) }; + } + + if (useLsd) + { + for (int i = 0; i < result.count() - 1; ++i) + { + auto& lineA = result[i]; + auto& lineB = result[i + 1]; + + QPointF intersectionPoint {}; + + if (lineA.intersects(lineB, &intersectionPoint) != QLineF::NoIntersection) + { + lineA.setP2(intersectionPoint); + lineB.setP1(intersectionPoint); + } + } + } + + // qDebug() << result; + + return result; +} diff --git a/src/imagealgos.h b/src/imagealgos.h new file mode 100644 index 0000000..4fd9d53 --- /dev/null +++ b/src/imagealgos.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#include "image.h" +#include "pixels.h" + +// size_t pgm_save(std::shared_ptr img, +size_t pgm_save(Image *img, FILE *outfile = nullptr, bool really_save = false); + +// void unpack_10bit(uint8_t const *src, Image const &image, uint16_t *dest); +// void unpack_16bit(uint8_t const *src, Image const &image, uint16_t *dest); + +// Pixels process_columns(Image & image); + +QList pixelsToLines(const Pixels& rawProfile); +QList pointsToLines(const QList& points); diff --git a/src/macro.h b/src/macro.h new file mode 100644 index 0000000..9bcc8e0 --- /dev/null +++ b/src/macro.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +#define start_timer(name) \ + std::chrono::steady_clock::time_point begin_##name = std::chrono::steady_clock::now(); + +#define stop_timer(name) \ + std::chrono::steady_clock::time_point end_##name = std::chrono::steady_clock::now(); \ + // std::cout << #name << " (us): " \ + // << std::chrono::duration_cast(end_##name - begin_##name) \ + // << std::endl; + +#define INIT_FIELD(name) m_##name(name) + +#define INNO_MAKER diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..d986f91 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,798 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "LibCamera.h" +#include "calibration.h" +#include "camera/innomakerov9281.h" +#include "camera/ov9281.h" +#include "dumps.h" +#include "fuck_intel.h" +#include "genetic_algos.h" +#include "httpservice.h" +#include "imagealgos.h" +#include "laser.h" +#include "macro.h" +#include "pigpio.h" +#include "printerclient.h" +#include "profile.h" +#include "rotaryencoder.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define try_apply_config() \ + if (!applyConfig(config)) \ + { \ + camera->release(); \ + cm->stop(); \ +\ + return EXIT_FAILURE; \ + } + +ScanningModeFlags scanningModeFlags{ScanningModeFlags::None}; + +QElapsedTimer calibrationTimer; + +extern volatile int32_t positionSteps; + +requested_params_t requested_params; + +namespace { +// std::shared_ptr img; +Image *img = nullptr; +Pixels pixels; +std::vector calibrationPixels; +QMutex calibrationPixelsMutex; +} // namespace + +using namespace std::chrono_literals; + +// static std::shared_ptr camera; +// std::unique_ptr config; +// static std::map> mappedBuffers_; +// std::vector> requests; +libcamera::ControlList lastControls; + +namespace { +CalibrationTablePtr calibrationTableZ; +CalibrationTablePtr calibrationTableX; +} // namespace + +// static bool applyConfig( +// const std::unique_ptr& config +// ); +// static void onRequestCompleted(libcamera::Request* completed_request); +// static void printControls(); +// static QList filter(const QList& rawProfiles); + +auto printPixels = [](const auto& pixels) { + for (size_t i = (img_width - 10) / 2; + i < img_width - ((img_width - 10) / 2); + ++i) + { + std::cout << pixels[i] << " "; + } + std::cout << std::endl; +}; + +// void onNewImage(std::shared_ptr image) +void onNewImage(Image &image) +{ + // std::cout << __func__ << std::endl << std::flush; + + // if (!image) + // { + // qDebug() << __func__ << "no image"; + // return; + // } + + ::img = ℑ +} + +void onNewPixels(std::shared_ptr pixels) +{ + // std::cout << __func__ << std::endl << std::flush; + + if (!pixels) + { + qDebug() << __func__ << "got null pixels"; + } + + if (!*pixels) + { + // qDebug() << __func__ << "got empty pixels"; + } + + for (size_t i = 640 - 5; i < 640 + 5; i++) + { + // std::cout << pixels->pixels[i] << " "; + } + + // std::cout << std::endl + + ::pixels = *pixels; +} + +bool initLaser(); + +int main(int argc, char* argv[]) +{ + QCoreApplication app(argc, argv); + + QList> initializers; + +#ifdef INNO_MAKER + if (false) + { + std::cout << std::boolalpha; + InnoMakerOV9281 innoMakerCam; + qDebug() << "init:" << innoMakerCam.init(); + qDebug() << "set exposure:" << innoMakerCam.setExposureTimeUs(3000000); + qDebug() << "set gain:" << innoMakerCam.setGain(2); + + innoMakerCam.startStream(); + QThread::sleep(3); + qDebug() << "should be stopped"; + // Image buf; + + // for (size_t i = 0; i < 1000; ++i) { + // if (!innoMakerCam.getImage(buf)) { + // break; + // } + + // buf.rotate(); + // auto pixels = buf.pixels(); + // } + } + // qDebug() << "ok"; + // exit(EXIT_SUCCESS); +#endif + + // if (false) + qDebug() << "size of raw profile" << sizeof(Pixels); + if (true) + { + // open binary calibration table + if (true) + { + initializers << QtConcurrent::run([&]() { + if (!openCalibrationTable( + // "/home/user/dumps/binz.calibration_table", + "/tmp/binz.calibration_table", + ::calibrationTableZ)) { + exit(EXIT_FAILURE); + } + }); + + initializers << QtConcurrent::run([&]() { + if (!openCalibrationTable( + // "/home/user/dumps/binx.calibration_table", + "/tmp/binx.calibration_table", + ::calibrationTableX)) { + exit(EXIT_FAILURE); + } + }); + } + + if (false) + { + // z + // if (!openCalibrationTable( + // "/home/user/dumps/binz.calibration_table", + // ::calibrationTableZ + // )) + // { + // exit(EXIT_FAILURE); + // } + + // if (!calibrationTableToImage(::calibrationTableZ) + // .save("/home/user/dumps/imageZ.png")) + // { + // qDebug() << "cannot save imageZ.png"; + // exit(EXIT_FAILURE); + // } + + // interpolate(::calibrationTableZ); + // exit(EXIT_SUCCESS); + + // calibrationTableToImage(::calibrationTableZ) + // .save("/home/user/dumps/imageZ_interpolated.png"); + + auto rawProfiles = openDump("/home/user/dumps/binx"); + qDebug() << "raw x-profiles count is" << rawProfiles.size(); + // qDebug() << "height" << calibrationColumnHeight; + + auto filteredRawProfiles = filter(std::move(rawProfiles)); + qDebug() << "filtered x-profiles count is" + << filteredRawProfiles.count(); + + ::calibrationTableX = calibrateX(std::move(filteredRawProfiles)); + + // for (size_t i = 9471; i < 9472; i++) { + // std::cout << "row #" << i << ": "; + + // for (size_t j = 0; j < 1280; ++j) { + // const auto& p = ::calibrationTableX->at(j).at(i); + // std::cout << p << ' '; + // } + + // std::cout << std::endl; + // } + + // x + // qDebug() << "open x table"; + // if (!openCalibrationTable("/home/user/dumps/binx.calibration_table", + // ::calibrationTableX)) { + // exit(EXIT_FAILURE); + // } + + // if (!calibrationTableToImage(::calibrationTableX) + // .save("/home/user/dumps/imageX.png")) { + // qDebug() << "cannot save imageX.png"; + // exit(EXIT_FAILURE); + // } + + // for (size_t i = 9471; i < 9472; i++) { + // std::cout << "row #" << i << ": "; + + // for (size_t j = 0; j < 1280; ++j) { + // const auto& p = ::calibrationTableX->at(j).at(i); + // std::cout << p << ' '; + // } + + // std::cout << std::endl; + // } + + // exit(EXIT_SUCCESS); + interpolate(::calibrationTableX); + + // calibrationTableToImage(::calibrationTableX) + // .save("/home/user/dumps/imageX_interpolated.png"); + } + + // load binary calibration dumps and calibrate + if (false) + { + if (true) + { + auto rawProfiles = openDump("/home/user/dumps/binz"); + // auto rawProfiles = openDump("/home/user/dumps/z"); + qDebug() << "raw z-profiles count is" << rawProfiles.size(); + // qDebug() << "height" << calibrationColumnHeight; + + auto filteredRawProfiles = filter(std::move(rawProfiles)); + qDebug() << "filtered z-profiles count is" + << filteredRawProfiles.count(); + + ::calibrationTableZ = calibrateZ(std::move(filteredRawProfiles), + requested_params.stepsPerMm); + + // bool ok = calibrationTableToImage(::calibrationTableZ) + // .save("/home/user/dumps/z/imageZ.png"); + + // if (!ok) + // { + // qDebug() << "cannot save imageZ.png"; + // exit(EXIT_FAILURE); + // } + + interpolate(::calibrationTableZ); + + if (!dump(::calibrationTableZ, + "/home/user/dumps/binz.calibration_table")) + { + qApp->exit(EXIT_FAILURE); + } + // calibrationTableToImage(::calibrationTableZ) + // .save("/home/user/dumps/z/imageZ_interpolated.png"); + // exit(EXIT_SUCCESS); + } + + qDebug() + << "--------------------------------------------------------"; + + if (true) + { + auto rawProfiles = openDump("/home/user/dumps/binx"); + qDebug() << "raw x-profiles count is" << rawProfiles.size(); + // qDebug() << "height" << calibrationColumnHeight; + + auto filteredRawProfiles = filter(std::move(rawProfiles)); + qDebug() << "filtered x-profiles count is" + << filteredRawProfiles.count(); + + ::calibrationTableX = calibrateX(std::move(filteredRawProfiles)); + + // bool ok = calibrationTableToImage(::calibrationTableX) + // .save("/home/user/dumps/z/imageX.png"); + + // if (!ok) + // { + // qDebug() << "cannot save imageX.png"; + // exit(EXIT_FAILURE); + // } + + interpolate(::calibrationTableX); + + if (!dump(::calibrationTableX, + "/home/user/dumps/binx.calibration_table")) + { + qApp->exit(EXIT_FAILURE); + } + + // calibrationTableToImage(::calibrationTableX) + // .save("/home/user/dumps/z/imageX_interpolated.png"); + } + } + } + + // exit(EXIT_SUCCESS); + + // if (!initLaser()) { + // return EXIT_FAILURE; + // } + + // PrinterClient printerClient; + + QElapsedTimer t; + t.start(); + + qDebug() << "msecs before encoder:" << t.elapsed(); + + // RotaryEncoder encoder; + + qDebug() << "msecs before camera:" << t.elapsed(); + // FIXME: don't use one var for everything + int ret; +#ifndef INNO_MAKER + std::unique_ptr cm = + std::make_unique(); + cm->start(); +#endif + // const auto cameras = cm->cameras(); + // const auto cameras = OV9281::search(cm); + const auto cameras = InnoMakerOV9281::search(); + // const auto cameras = + + if (cameras.empty()) + { + std::cerr << "No cameras were identified on the system." << std::endl; +#ifndef INNO_MAKER + cm->stop(); +#endif + + return EXIT_FAILURE; + } + + auto camera = cameras.at(0); + +#ifndef INNO_MAKER + camera->printControls(); +#endif + + std::cout << "connect everything" << std::endl; + // camera->newPixels.connect(&onNewPixels); + // camera->newImage.connect(&onNewImage); + camera->newImageCallback = &onNewImage; + camera->newPixelsCallback = &onNewPixels; + + for (auto& i : initializers) + i.waitForFinished(); + std::cout << "loaded calibration tables" << std::endl; + + if (!camera->startStream()) + { +#ifndef INNO_MAKER + cm->stop(); +#endif + + return EXIT_FAILURE; + } + + QHttpServer qHttpServer; + qHttpServer.route("/v1/sensor/image", [&]() { + // std::cout << "http: image" << std::endl << std::flush; + // FILE *f = fopen("/tmp/img.pgm", "w"); + // static bool save = false; + pgm_save(::img); + // save = false; + std::lock_guard lg(pgm_image_mtx); + // qDebug() << "mutex locked"; + // qDebug() << "image saved to array"; + return QByteArray((const char*) pgm_image, pgm_image_size); + }); + qHttpServer.route("/v1/sensor/image2", [&]() { + // std::cout << "http: image2" << std::endl; + pgm_save(::img); + + std::lock_guard lg(pgm_image_mtx); + // qDebug() << "image2"; + return QByteArray((const char*) pgm_image, pgm_image_size); + }); + // qHttpServer.route("/v1/sensor/exposureTimeUs", [&]() { + // // std::lock_guard lg(pgm_image_mtx); + // return "123"; + // }); + qHttpServer.route("/v1/pixels", [&]() { + // std::cout << "http: pixels" << std::endl; + std::lock_guard lg(pgm_image_mtx); + + QJsonArray pixels; + + for (size_t i = 0; i < img_width; ++i) + { + // pixels << img_height - img.pixels[i]; + pixels << ::pixels.pixels[i]; + } + + QJsonObject json; + json["pixels"] = pixels; + // json["encoderPosition"] = qint64{encoder.position()}; + // FIXME: get prom pixels struct + json["measurementCounter"] = qint64{img->counters.measurementCounter}; + json["timestampUs"] = qint64(img->counters.timestampUs); + + const auto lines = pixelsToLines(::pixels); + + // qDebug() << "lines count is " << lines.count(); + + QJsonArray jsonLines; + + for (const auto& l : lines) + { + jsonLines << QJsonArray{QJsonArray{l.p1().x(), l.p1().y()}, + QJsonArray{l.p2().x(), l.p2().y()}}; + } + + json["lines"] = jsonLines; + + return QHttpServerResponse(QJsonDocument(json).toJson()); + }); + + qHttpServer.route("/v1/profile", [&]() -> QHttpServerResponse { + // std::cout << "http: profile" << std::endl; + std::lock_guard lg(pgm_image_mtx); + + if (!::calibrationTableZ || !::calibrationTableX) + return QHttpServerResponse::StatusCode::ServiceUnavailable; + + const Profile profile(::pixels, + ::calibrationTableZ, + ::calibrationTableX); + + const QJsonObject json{{"profile", QJsonObject(profile)}}; + + return QHttpServerResponse(QJsonDocument(json).toJson()); + }); + + qHttpServer + .route("/v1/commands/resetEncoder", + [&](const QHttpServerRequest& request) -> QHttpServerResponse { + std::cout << "http: resetEncoder" << std::endl; + if (request.method() != QHttpServerRequest::Method::Post) + { + return QHttpServerResponse::StatusCode::NotFound; + } + + qDebug() << "reset encoder"; + + positionSteps = 0; + + return QHttpServerResponse::StatusCode::Ok; + }); + + qHttpServer + .route("/v1/commands/startCalibration", + [&](const QHttpServerRequest& request) -> QHttpServerResponse { + std::cout << "http: startCalibration" << std::endl; + if (request.method() != QHttpServerRequest::Method::Post) + { + return QHttpServerResponse::StatusCode::NotFound; + } + + qDebug() << "start calibration"; + + // TODO: use flags + scanningModeFlags = ScanningModeFlags::Calibration; + calibrationTimer.start(); + + return QHttpServerResponse::StatusCode::Ok; + }); + + qHttpServer + .route("/v1/commands/gCode", + [&](const QHttpServerRequest& request) -> QHttpServerResponse { + std::cout << "http: gCode" << std::endl; + if (request.method() != QHttpServerRequest::Method::Post) + { + return QHttpServerResponse::StatusCode::NotFound; + } + + const auto command = request.body(); + + qDebug() << "send gCode:" << command; + + // printerClient.sendCommand(command); + + return QHttpServerResponse::StatusCode::Ok; + }); + + // qHttpServer + // .route("/v1/commands/startCalibration", + // [&](const QHttpServerRequest& request) -> QHttpServerResponse { + // std::cout << "http: startCalibration" << std::endl; + // if (request.method() != QHttpServerRequest::Method::Post) + // { + // return QHttpServerResponse::StatusCode::NotFound; + // } + + // const auto command = request.body(); + + // qDebug() << "send gCode:" << command; + + // // printerClient.sendCommand(command); + + // return QHttpServerResponse::StatusCode::Ok; + // }); + + qHttpServer.route( + "/v1/sensor/params", + [&](const QHttpServerRequest& request) -> QHttpServerResponse { + // std::cout << "http: params" << std::endl; + switch (request.method()) + { + case QHttpServerRequest::Method::Get: { + std::lock_guard lg(pgm_image_mtx); + QJsonObject json; + + // const libcamera::ControlIdMap& ctrlIdMap = + // camera->controls().idmap(); + + // qDebug() << "readParams:" << lastControls.size(); + // qDebug() << request.method(); + + // for (const auto& [id, value] : lastControls) + // { + // const libcamera::ControlId* controlId = ctrlIdMap.at(id); + // auto name = QString::fromStdString(controlId->name()); + // const auto valueStr = + // QString::fromStdString(value.toString()); + // qDebug() + // << "\t param:" << controlId->id() << name << valueStr; + + // name[0] = name[0].toLower(); + // json[name] = valueStr; + // } + + // json[laserLevelKey] = requested_params.laserLevel; + + // qDebug() << "response body:" << json; + + // QHttpServerResponse + return QHttpServerResponse(QJsonDocument(json).toJson()); + } + + case QHttpServerRequest::Method::Post: { + // qDebug() << "request body:" << request.body(); + + auto json = QJsonDocument::fromJson(request.body()).object(); + + if (json.contains(exposureTimeKey)) + { + const int32_t value{json[exposureTimeKey].toInt()}; + + if (value == 0) + return QHttpServerResponse::StatusCode:: + RequestRangeNotSatisfiable; + + // qDebug() << "set new exposure time:" << value; + + // requested_params.exposureTime = value; + if (!camera->setExposureTimeUs(value)) + return QHttpServerResponse::StatusCode:: + RequestRangeNotSatisfiable; + } + + if (json.contains(gainKey)) + { + const int32_t value{json[gainKey].toInt()}; + + if (value == 0) + return QHttpServerResponse::StatusCode:: + RequestRangeNotSatisfiable; + + // qDebug() << "set gain:" << value; + + // requested_params.exposureTime = value; + if (!camera->setGain(value)) + return QHttpServerResponse::StatusCode:: + RequestRangeNotSatisfiable; + } + + if (json.contains(laserLevelKey)) + { + const int32_t value{json[laserLevelKey].toInt()}; + + // if (value == 0) + // { + // return QHttpServerResponse::StatusCode::NotFound; + // } + + // qDebug() << "set new laserLevel:" << value; + if (!camera->setLaserLevel(value)) + return QHttpServerResponse::StatusCode:: + RequestRangeNotSatisfiable; + + requested_params.laserLevel = value; + + // const QString laserLevelFile{ + // "/sys/class/pwm/pwmchip2/pwm1/duty_cycle"}; + // QFile f{laserLevelFile}; + + // if (!f.open(QFile::ReadWrite)) + // { + // qDebug() << "cannot open laser level file:" + // << f.errorString(); + // qDebug() << "file path is" << f.fileName(); + // return QHttpServerResponse::StatusCode::InternalServerError; + // } + + // QTextStream s{&f}; + + // s << value; + + // s >> requested_params.laserLevel; + + // qDebug() << "done with laser level"; + } + + return QHttpServerResponse(request.body()); + } + default: { + return QHttpServerResponse( + QByteArray("unsupported http method")); + } + } + }); + + qDebug() << "listen: " << qHttpServer.listen(QHostAddress::Any, 8081); + + QFuture future = QtConcurrent::run([]() { + Port port(8080); + Address addr(Ipv4::any(), port); + + HttpService httpService(addr); + + size_t threads_count = 1; + httpService.init(threads_count); + httpService.start(); + }); + + //////////////////////////////////////////////////////////////////////////// + std::clog << std::flush; + std::cerr << std::flush; + std::cout << "ok for now" << std::endl << std::flush; + + // camera->stop(); + // camera->release(); + // cm->stop(); + + auto result = app.exec(); + + future.cancel(); + future.waitForFinished(); + + // for (auto& [fd, mem] : mappedBuffers_) + // { + // munmap(mem.first, mem.second); + // } + + // FIXME: crash somewhere here. proper libcamera finishing needed + // requests.clear(); + // mappedBuffers_.clear(); + + // camera->stop(); + // config.reset(); + // allocator->free(stream); + // allocator.reset(); + // camera->release(); + // camera.reset(); +#ifndef INNO_MAKER + cm->stop(); +#endif + + return result; +} + +bool initLaser() +{ + const QLatin1String pwmChip{"pwmchip2"}; + const uint16_t pwmChannel{1}; + const QLatin1String pwmSystemRoot{"/sys/class/pwm"}; + const QString pwmChipRoot{pwmSystemRoot + "/" + pwmChip}; + + const QString pwmExportFile{pwmChipRoot + "/export"}; + + QFile f{pwmExportFile}; + + if (!f.open(QFile::WriteOnly)) + { + qWarning() << "cannot open" << f.fileName() << "for writing"; + qWarning() << "error:" << f.errorString(); + + return false; + } + + QTextStream s{&f}; + s << pwmChannel; + + const QString pwm{QLatin1String("pwm%1").arg(QString::number(pwmChannel))}; + const QString pwmRoot{pwmChipRoot + "/" + pwm}; + + const QString periodFilename{pwmRoot + "/period"}; + f.close(); + f.setFileName(periodFilename); + + if (!f.open(QFile::WriteOnly)) + { + qWarning() << "cannot open" << f.fileName() << "for writing"; + qWarning() << "error:" << f.errorString(); + + return false; + } + + const unsigned periodHz{50'000}; + + s << periodHz; + + const QString dutyCycleFilename{pwmRoot + "/duty_cycle"}; + f.close(); + f.setFileName(dutyCycleFilename); + + if (!f.open(QFile::WriteOnly)) + { + qWarning() << "cannot open" << f.fileName() << "for writing"; + qWarning() << "error:" << f.errorString(); + + return false; + } + + const unsigned dutyCycle{3'000}; + + s << dutyCycle; + + const QString enableFilename{pwmRoot + "/enable"}; + f.close(); + f.setFileName(enableFilename); + + if (!f.open(QFile::WriteOnly)) + { + qWarning() << "cannot open" << f.fileName() << "for writing"; + qWarning() << "error:" << f.errorString(); + + return false; + } + + const int enable{1}; + + s << enable; + + return true; +} diff --git a/src/printerclient.cpp b/src/printerclient.cpp new file mode 100644 index 0000000..4fed38f --- /dev/null +++ b/src/printerclient.cpp @@ -0,0 +1,86 @@ +#include "printerclient.h" + +#include + +#include +#include +#include +#include + +QString getFirstTtyUSB() +{ + auto ports = QSerialPortInfo::availablePorts(); + + std::remove_if(ports.begin(), ports.end(), [](const auto& port) { + return !port.portName().contains("ttyUSB"); + }); + + return ports.isEmpty() ? "" : ports.first().portName(); +} + +PrinterClient::PrinterClient(QObject* parent) + : QObject{parent} // , m_serialPort{new QSerialPort{"/dev/ttyUSB0", this}} + , m_serialPort{new QSerialPort{getFirstTtyUSB(), this}} +{ + if (!m_serialPort->setBaudRate(QSerialPort::Baud115200)) { + throw std::runtime_error( + "serial port: cannot set baud rate: " + + m_serialPort->errorString().toStdString()); + + return; + } + + if (!m_serialPort->open(QFile::ReadWrite)) { + throw std::runtime_error( + "cannot open serial port: " + + m_serialPort->errorString().toStdString()); + + return; + } + + qDebug() << "serial port baud rate:" << m_serialPort->baudRate(); + + qDebug() << "serial port data bits:" << m_serialPort->dataBits(); + qDebug() << "serial port parity:" << m_serialPort->parity(); + qDebug() << "serial port stop bits:" << m_serialPort->stopBits(); + + QObject::connect(m_serialPort, &QSerialPort::readyRead, + this, &PrinterClient::onReadyRead); + + QObject::connect(m_serialPort, &QSerialPort::errorOccurred, + this, &PrinterClient::onErrorOccured); + + // m_serialPort->write(QByteArray { "G91\n" }); + // m_serialPort->flush(); + // m_serialPort->write(QByteArray { "G1 Z10\n" }); + // m_serialPort->flush(); + sendCommand("G91"); + // sendCommand("G1 Z10"); + // sendCommand("G1 Z-10"); + + onErrorOccured(QSerialPort::SerialPortError::PermissionError); +} + +void PrinterClient::sendCommand(const QString command) +{ + const auto written = m_serialPort->write(command.toUtf8() + "\n"); + + qDebug() << QString("serialPort: send '%1': (written %2 bytes)") + .arg(command).arg(written); + + m_serialPort->flush(); +} + +void PrinterClient::onReadyRead() +{ + const auto data = m_serialPort->readAll(); + qDebug() << "serialPort: " << data; + + // emit newData(data); +} + +void PrinterClient::onErrorOccured(QSerialPort::SerialPortError error) +{ + qWarning() << "serial port error:" << m_serialPort->errorString() + << "-" << error; +} diff --git a/src/printerclient.h b/src/printerclient.h new file mode 100644 index 0000000..d1266dd --- /dev/null +++ b/src/printerclient.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include + +class QSerialPort; + +class PrinterClient : public QObject +{ + // Q_OBJECT + +public: + explicit PrinterClient(QObject *parent = nullptr); + // ~PrinterClient() override = default; + // ~PrinterClient + + // signals: + // void newData(const QString output); + +public: + void sendCommand(const QString command); + +private: + void onReadyRead(); + void onErrorOccured(QSerialPort::SerialPortError error); + +private: + QSerialPort* m_serialPort { nullptr }; +}; diff --git a/src/profile.cpp b/src/profile.cpp new file mode 100644 index 0000000..2c7b9a9 --- /dev/null +++ b/src/profile.cpp @@ -0,0 +1,155 @@ +#include "profile.h" + +#include + +#include +#include + +Profile::Profile( + const Pixels& pixels, + const CalibrationTablePtr calibrationTableZ, + const CalibrationTablePtr calibrationTableX) + : m_counters(pixels.counters) +{ + if (!calibrationTableZ || !calibrationTableX) + { + std::cerr << __func__ << ": got invalid calibration tables" + << std::endl; + + return; + } + + static bool done{false}; + + if (!done) { + for (size_t i = 9471; i < 9472; i++) { + std::cout << __func__ << ": row #" << i << ": "; + + for (size_t j = 0; j < 1280; ++j) { + const auto& x = calibrationTableX->at(j).at(i); + const auto& z = calibrationTableZ->at(j).at(i); + std::cout << "Profile: table: " << x << ' ' << z << std::endl; + } + + std::cout << std::endl; + } + } + + for (size_t i = 0; i < img_width; ++i) { + try { + const auto& pixel = pixels.pixels[i]; + const auto pixelDiscrete = pixel * discretesInRage / img_height; + if (Q_UNLIKELY(pixel >= sizeof(CalibrationColumn) / sizeof(float))) { + qWarning() << "got invalid calibration pixel at" << i << ":" + << pixel; + m_counters = {}; + m_pointsMm = {QPointF{std::nan(""), std::nan("")}}; + return; + } + + if (Q_UNLIKELY(pixel == sizeof(CalibrationColumn) - 1)) { + qDebug() << "Profile: got edge value"; + const auto& z = calibrationTableZ->at(i).at(pixelDiscrete); + + if (qFuzzyIsNull(z) || std::isnan(z)) { + m_pointsMm.at(i) = {std::nan(""), std::nan("")}; + continue; + } + + const auto& x = calibrationTableX->at(i).at(pixelDiscrete); + m_pointsMm.at(i) = {x, z}; + + continue; + } + + const auto& leftMmZ = calibrationTableZ->at(i).at( + size_t(pixelDiscrete)); + const auto& rightMmZ = calibrationTableZ->at(i).at( + size_t(pixelDiscrete) + 1); + + const auto& leftMmX = calibrationTableX->at(i).at( + size_t(pixelDiscrete)); + const auto& rightMmX = calibrationTableX->at(i).at( + size_t(pixelDiscrete) + 1); + + const auto& fract = pixelDiscrete - long(pixelDiscrete); + + const auto z = (leftMmZ * (1 - fract) + rightMmZ * fract); + + // TODO: use only NaN (or zero?) everywhere + // NOTE: QJsonValue converts NaN to zero + if (qFuzzyIsNull(z) || std::isnan(z)) { + // qDebug() << "got nan z for discrete" << pixelDiscrete << leftMmZ + // << rightMmZ; + m_pointsMm.at(i) = {std::nan(""), std::nan("")}; + continue; + } + + const auto x = (leftMmX * (1 - fract) + rightMmX * fract); + + m_pointsMm.at(i) = {x, z}; + + if (!done) { + qDebug() << "got these values\t" << pixels.pixels[i] + << pixelDiscrete << m_pointsMm[i] << leftMmZ + << rightMmZ << leftMmX << rightMmX; + } + } catch (const std::out_of_range& ex) { + qWarning() << "out of range exception:" << ex.what(); + qWarning() << "i:" << i; // << "pixels[i]" << pixels.pixels[i]; + m_counters = {}; + m_pointsMm = {QPointF{std::nan(""), std::nan("")}}; + + return; + } catch (const std::exception& ex) { + qWarning() << "exception:" << ex.what(); + qWarning() << "i:" << i << "pixels[i]" << pixels.pixels[i]; + m_counters = {}; + m_pointsMm = {QPointF{std::nan(""), std::nan("")}}; + + return; + } catch (...) { + qWarning() << "unknown exception"; + m_counters = {}; + m_pointsMm = {QPointF{std::nan(""), std::nan("")}}; + return; + } + } + + done = true; + + // static bool printOnce = [&]() -> bool { + // for (size_t i = 0; i < m_pointsMm.size(); ++i) { + // qDebug() << '\t' << pixels.pixels[i] << m_pointsMm[i]; + // } + + // return true; + // }(); +} + +const Counters& Profile::counters() const +{ + return m_counters; +} + +const Profile::PointsMm& Profile::pointsMm() const +{ + return m_pointsMm; +} + +Profile::operator const QJsonObject() const +{ + QJsonObject counters{ + {"timestampUs", qint64(m_counters.timestampUs)}, + {"measurementCounter", qint64(m_counters.measurementCounter)}, + {"encoderPosition", qint64(m_counters.encoderPosition)}, + }; + + QJsonArray jsonPoints; + + for (const auto& p : m_pointsMm) { + jsonPoints << QJsonArray{p.x(), p.y()}; + } + + return {{"counters", counters}, {"pointsMm", jsonPoints}}; +} diff --git a/src/profile.h b/src/profile.h new file mode 100644 index 0000000..0e2f839 --- /dev/null +++ b/src/profile.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +#include "calibration.h" +#include "pixels.h" + +class Profile +{ +public: + using PointsMm = std::array; + +public: + // TODO: make private/protected + explicit Profile(const Pixels& pixels, + const CalibrationTablePtr calibrationTableZ, + const CalibrationTablePtr calibrationTableX); + +public: + const Counters& counters() const; + const PointsMm& pointsMm() const; + + operator const QJsonObject() const; + +private: + Counters m_counters{}; + PointsMm m_pointsMm{QPointF{std::nan(""), std::nan("")}}; +}; diff --git a/src/rotaryencoder.cpp b/src/rotaryencoder.cpp new file mode 100644 index 0000000..62ca33d --- /dev/null +++ b/src/rotaryencoder.cpp @@ -0,0 +1,125 @@ +#include "rotaryencoder.h" + +#include +#include +#include +#include + +#include +#include + +#include + +// got from https://gist.github.com/ast/a19813fce9d34c7240091db11b8190dd +// https://gist.github.com/ast +// Inspired by Paul Stoffregen's excellent Arduino library Encoder: +// https://github.com/PaulStoffregen/Encoder + +constexpr int gpioA = 17; +constexpr int gpioB = 27; +// constexpr int gpioA = 0; +// constexpr int gpioB = 2; +// constexpr int gpioA = 11; +// constexpr int gpioB = 13; + +const std::vector encoderTable = { + 0, 1, -1, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, -1, 1, 0 +}; + +volatile int32_t positionSteps; +volatile uint8_t state; + +void pin_isr(void) { + uint8_t p1val = digitalRead(gpioA); + uint8_t p2val = digitalRead(gpioB); + uint8_t s = state & 3; + + if (p1val) s |= 4; + if (p2val) s |= 8; + + state = (s >> 2); + + switch (s) { + case 1: case 7: case 8: case 14: + positionSteps = positionSteps + 1; + return; + case 2: case 4: case 11: case 13: + positionSteps = positionSteps - 1; + return; + case 3: case 12: + positionSteps = positionSteps + 2; + return; + case 6: case 9: + positionSteps = positionSteps - 2; + return; + } +} + +RotaryEncoder::RotaryEncoder() +{ + if (!m_self) { + m_self = this; + } else { + qWarning() << "normally there should be only one instance of RotaryEncoder"; + } + + QElapsedTimer t; + t.start(); + + if (wiringPiSetupGpio()) { + perror("wiringPiSetupGpio"); + exit(EXIT_FAILURE); + } + + qDebug() << "msecs to setup wiringPi:" << t.elapsed(); + + if ( wiringPiISR (gpioA, INT_EDGE_BOTH, &pin_isr) < 0 ) { + perror("wiringPiISR"); + exit(EXIT_FAILURE); + } + + qDebug() << "msecs to register interruption A:" << t.elapsed(); + + if ( wiringPiISR (gpioB, INT_EDGE_BOTH, &pin_isr) < 0 ) { + perror("wiringPiISR"); + exit(EXIT_FAILURE); + } + + qDebug() << "msecs to register interruption B:" << t.elapsed(); + + // pinMode (gpioA, INPUT) ; + // pinMode (gpioB, INPUT) ; + // pullUpDnControl(gpioA, PUD_UP); + // pullUpDnControl(gpioB, PUD_UP); + + qDebug() << "encoder is ok"; + + // // Show position every second + // while ( 1 ) { + // constexpr double stepsPerMm = 200; + // const double positionMm = ::positionSteps / stepsPerMm; + // qDebug() << ::positionSteps + // << '-' + // << QString::number(positionMm, 'f', 3) + // << "(mm)"; + // // printf( "%ld\n", ::position); + // usleep( 100 * 1000 ); // wait 1 second + // } +} + +RotaryEncoder::~RotaryEncoder() +{ + if (m_self == this) { + m_self = nullptr; + } +} + +RotaryEncoder *RotaryEncoder::instance() +{ + return m_self; +} + +int32_t RotaryEncoder::position() const +{ + return ::positionSteps; +} diff --git a/src/rotaryencoder.h b/src/rotaryencoder.h new file mode 100644 index 0000000..c0c39d8 --- /dev/null +++ b/src/rotaryencoder.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +// TODO: delete singleton functionality +class RotaryEncoder final +{ +public: + RotaryEncoder(); + ~RotaryEncoder(); + +public: + static RotaryEncoder* instance(); + +public: + int32_t position() const; + +private: + static inline RotaryEncoder* m_self { nullptr }; +}; -- cgit v1.2.3-70-g09d2