diff options
| -rw-r--r-- | src/camera/icamera.h | 39 | ||||
| -rw-r--r-- | src/camera/innomakerov9281.cpp | 523 | ||||
| -rw-r--r-- | src/camera/innomakerov9281.h | 63 | ||||
| -rw-r--r-- | src/camera/ov9281.cpp | 419 | ||||
| -rw-r--r-- | src/camera/ov9281.h | 90 | ||||
| -rw-r--r-- | src/camera/veye_i2c.cpp | 78 | ||||
| -rw-r--r-- | src/camera/veye_i2c.h | 25 | ||||
| -rw-r--r-- | src/camera/veyeimx287m.cpp | 252 | ||||
| -rw-r--r-- | src/camera/veyeimx287m.h | 32 | ||||
| -rw-r--r-- | src/camera/veyeimx287m_types.h | 14 | ||||
| -rw-r--r-- | src/iscanner.cpp | 5 | ||||
| -rw-r--r-- | src/iscanner.h | 19 | ||||
| -rw-r--r-- | src/macro.h | 2 | ||||
| -rw-r--r-- | src/main.cpp | 61 | ||||
| -rw-r--r-- | src/protocols/httpserver.cpp (renamed from src/httpservice.cpp) | 99 | ||||
| -rw-r--r-- | src/protocols/httpserver.h (renamed from src/httpservice.h) | 11 | ||||
| -rw-r--r-- | src/protocols/iprotocol.cpp | 5 | ||||
| -rw-r--r-- | src/protocols/iprotocol.h | 19 | ||||
| -rw-r--r-- | src/protocols/protocolbase.cpp | 3 | ||||
| -rw-r--r-- | src/protocols/protocolbase.h | 10 |
20 files changed, 413 insertions, 1356 deletions
diff --git a/src/camera/icamera.h b/src/camera/icamera.h index 39d358f..391ce55 100644 --- a/src/camera/icamera.h +++ b/src/camera/icamera.h @@ -20,14 +20,11 @@ #define slots slots_backup #endif -#include "image.h" +// cpp +#include <chrono> -// class ICamera -// { -// public: -// virtual bool setExposureTimeMs(int value) = 0; -// virtual bool setGain(int value) = 0; -// }; +// orpheus +#include "image.h" class ICamera { @@ -35,16 +32,32 @@ public: virtual ~ICamera() = default; public: - virtual bool setExposureTimeUs(int value) = 0; - virtual bool setGain(int value) = 0; + virtual bool set_autoExposure(const bool enable) = 0; + /*! + * \brief get_autoExposure - check if auto exposure is enabled + * \param ok - indicates successful read + * \return true if enabled, false otherwise + */ + + /*! + * \brief get_autoExposure - check if auto exposure is enabled + * \return non-empty value on success, empty value on failure + */ + virtual std::optional<bool> get_autoExposure() = 0; + + virtual bool set_exposureTime(const std::chrono::microseconds us) = 0; + virtual std::optional<const std::chrono::microseconds> get_exposureTime() = 0; + + virtual bool set_autoGain(const bool enable) = 0; + virtual std::optional<bool> get_autoGain() = 0; + + virtual bool set_gain(const float value) = 0; + virtual std::optional<float> get_gain() = 0; + virtual bool setLaserLevel(int value) = 0; - virtual bool setSomething(int value) = 0; public: - libcamera::Signal<std::shared_ptr<Pixels>> newPixels; - libcamera::Signal<std::shared_ptr<Image>> newImage; std::function<void(std::shared_ptr<Pixels>)> newPixelsCallback; - // std::function<void(std::shared_ptr<Image>)> newImageCallback; std::function<void(Image &)> newImageCallback; public: diff --git a/src/camera/innomakerov9281.cpp b/src/camera/innomakerov9281.cpp deleted file mode 100644 index 184601a..0000000 --- a/src/camera/innomakerov9281.cpp +++ /dev/null @@ -1,523 +0,0 @@ -#include "innomakerov9281.h" - -#include <errno.h> -#include <fcntl.h> -#include <linux/videodev2.h> -#include <stdio.h> -#include <string.h> -#include <sys/ioctl.h> -#include <sys/mman.h> -#include <unistd.h> - -#include <iostream> -#include <span> - -#include <QElapsedTimer> - -#include "constants.h" -// #include "rotaryencoder.h" - -#define LOGD(...) \ - do \ - { \ - printf(__VA_ARGS__); \ - printf("\n"); \ - } while (0) - -#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; -extern uint64_t pix_elapsed_ns; -extern uint64_t dropped_count; - -// constexpr char videoDevice[] = "/dev/video0"; - -InnoMakerOV9281::InnoMakerOV9281() {} - -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) - { - ret = munmap(video_buffer_ptr[i], img_size); - - if (ret < 0) - { - DBG("Munmap failed!!."); - } - } - - // std::cout << __func__ << std::endl; - if (m_cam_fd >= 0) - { - int ret = close(m_cam_fd); - - if (ret == -1) - { - // std::cout << __func__ - // << ": cannot close camera: " << strerror(errno) - // << std::endl; - } - }; - - // std::cout << __func__ << ": success" << std::endl; -} - -std::vector<std::shared_ptr<ICamera> > InnoMakerOV9281::search() -{ - // return only one camera for now - std::cout << std::boolalpha; - auto cam = std::make_shared<InnoMakerOV9281>(); - - if (!cam->init()) - return {}; - - if (!cam->setExposureTimeUs(30)) - return {}; - - if (!cam->setLaserLevel(1)) - return {}; - - 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<Image> imageCopy = std::make_shared<Image>(); - while (!stopToken.stop_requested()) - { - size_t imageIndex{}; - - // if (!getImage(image)) - if (!getImage(imageIndex)) - break; - - continue; - - // 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); - // } - // // 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 - std::cout << "InnoMakerOV9281: stream interruption requested" - << std::endl; - }}; - - return true; -} - -bool InnoMakerOV9281::init() -{ - if (!openCam()) - return false; - - if (!selectCam()) - return false; - - if (!initCam()) - return false; - - return true; -} - -bool InnoMakerOV9281::setExposureTimeUs(int valueUs) -{ - return true; - std::cout << __func__ << ": " << valueUs << std::endl << std::flush; - - /* - * Shutter Time. Value is from 8721ns to 8721*885ns, must be integral - * multiple of 8721ns . - * 8721xN(N =1,2,3,4,5.....855) - */ - // constexpr int exposureStep{8721}; - // constexpr int maxExposureStepMultiplier{885}; - // auto valueNs = valueUs; - // valueNs = (valueNs / exposureStep) * exposureStep; - - // std::clamp(valueNs, exposureStep, exposureStep * maxExposureStepMultiplier); - // setGain(rand() % 254); - // setGain(3); - // setLaserLevel(rand() % 0x7fffffff); - // setLaserLevel(rand() % 100); - // int exp = rand() % 10; - // return setCamParam(V4L2_CID_EXPOSURE, exp * exp * exp * exp * exp * exp); - return setCamParam(V4L2_CID_EXPOSURE, valueUs); -} - -bool InnoMakerOV9281::setGain(int value) -{ - std::cout << __func__ << ": " << value << std::endl << std::flush; - - // return setCamParam(V4L2_CID_GAIN, value); - // FIXME: tmp workaround for imx287llr - return true; -} - -bool InnoMakerOV9281::setLaserLevel(int value) -{ - std::cout << __func__ << ": " << value << std::endl << std::flush; - - // return setCamParam(V4L2_CID_FLASH_TIMEOUT, value); - - // FIXME: tmp workaround for imx287llr - return true; -} - -bool InnoMakerOV9281::setSomething(int value) -{ - std::cout << __func__ << ": " << value << std::endl << std::flush; - - // return setCamParam(V4L2_CID_FLASH_INTENSITY, value); - // FIXME: tmp workaround for imx287llr - return true; -} - -bool InnoMakerOV9281::setCamParam(unsigned int v4l2controlId, int value) -{ - v4l2_control ctl{v4l2controlId, value}; - - int ret = ioctl(m_cam_fd, VIDIOC_S_CTRL, &ctl); - - if (ret < 0) - { - fprintf(stderr, - "cannot set cam param: id - %d, error - '%s'\n", - v4l2controlId, - strerror(errno)); - fflush(stderr); - - return false; - } - - ret = ioctl(m_cam_fd, VIDIOC_G_CTRL, &ctl); - - if (ret < 0) - { - fprintf(stderr, - "cannot get cam param: id - %d, error - '%s'\n", - v4l2controlId, - strerror(errno)); - - fflush(stderr); - - return false; - } - - std::cout << __func__ << ": new value is " << ctl.value << std::endl; - - return true; -} - -bool InnoMakerOV9281::openCam() -{ - m_cam_fd = open(videoDevice, O_RDWR); - - if (m_cam_fd < 0) - { - fprintf(stderr, - "cannot open cam '%s', error: '%s'\n", - videoDevice, - strerror(errno)); - return false; - } - - return true; -} - -bool InnoMakerOV9281::selectCam(int camIdx) -{ - int input = camIdx; - int ret = ioctl(m_cam_fd, VIDIOC_S_INPUT, &input); - - if (ret < 0) - { - fprintf(stderr, - "cannot select cam: idx - %d, error - '%s'\n", - camIdx, - strerror(errno)); - - return false; - } - - return true; -} - -bool InnoMakerOV9281::initCam() -{ - v4l2_format format; - memset(&format, 0, sizeof(v4l2_format)); - format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - format.fmt.pix.pixelformat = V4L2_PIX_FMT_GREY; - format.fmt.pix.width = img_width; - format.fmt.pix.height = img_height; - - int ret = ioctl(m_cam_fd, VIDIOC_TRY_FMT, &format); - - if (ret < 0) - { - fprintf(stderr, - "cannot try cam format: error - '%s'\n", - strerror(errno)); - - return false; - } - - // TODO: remove this? - format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - ret = ioctl(m_cam_fd, VIDIOC_S_FMT, &format); - - if (ret < 0) - { - fprintf(stderr, - "cannot set cam format: error - '%s'\n", - strerror(errno)); - - return false; - } - - struct v4l2_requestbuffers request; - request.count = BUFFER_COUNT; - request.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - 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)); - - return false; - } - - if (request.count < BUFFER_COUNT) - { - fprintf(stderr, "cannot set cam request buffers\n"); - - return false; - } - - struct v4l2_buffer buffer; - memset(&buffer, 0, sizeof(buffer)); - buffer.type = request.type; - buffer.memory = V4L2_MEMORY_MMAP; - - for (uint32_t i = 0; i < request.count; i++) - { - buffer.index = i; - ret = ioctl(m_cam_fd, VIDIOC_QUERYBUF, &buffer); - - if (ret < 0) - { - DBG("ioctl(VIDIOC_QUERYBUF) failed %d(%s)", errno, strerror(errno)); - return false; - } - - 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) { - DBG("mmap() failed %d(%s)", errno, strerror(errno)); - return false; - } - - buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buffer.memory = V4L2_MEMORY_MMAP; - buffer.index = i; - ret = ioctl(m_cam_fd, VIDIOC_QBUF, &buffer); - if (ret != 0) - { - DBG("ioctl(VIDIOC_QBUF) 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; -} - -// 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; - gettimeofday(&curr, NULL); - ++counter; - - double elapsedTime = (curr.tv_sec - prev.tv_sec) * 1000.0; // sec to ms - elapsedTime += (curr.tv_usec - prev.tv_usec) / 1000.0; // us to ms - - if (elapsedTime > 1000.) - { - fprintf(stderr, - "fps: %d\tdropped: %d\tsec: %d\t" - "dq: %d\tget: %d\trot: %d\tpix: %d\tsum: %d,\tcorr: " - "%d,\tval: %d\n", - counter, - dropped_count, - curr.tv_sec % 1000, - dq_elapsed_ns / 1000 / counter, - get_elapsed_ns / 1000 / counter, - rot_elapsed_ns / 1000 / counter, - pix_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; - pix_elapsed_ns = 0; - dropped_count = 0; - - counter = 0; - prev = curr; - } - - int ret; - struct v4l2_buffer buffer; - - memset(&buffer, 0, sizeof(buffer)); - buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buffer.memory = V4L2_MEMORY_MMAP; - buffer.index = BUFFER_COUNT; - - { - 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; - } - - if (buffer.index < 0 || buffer.index >= BUFFER_COUNT) - { - DBG("invalid buffer index: %d", buffer.index); - 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; - - static int64_t prevCounter = buffer.sequence; - dropped_count += buffer.sequence - prevCounter - 1; - prevCounter = buffer.sequence; - - 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) { - DBG("ioctl(VIDIOC_QBUF) failed %d(%s)", errno, strerror(errno)); - return false; - } - - return true; -} diff --git a/src/camera/innomakerov9281.h b/src/camera/innomakerov9281.h deleted file mode 100644 index 7f2d206..0000000 --- a/src/camera/innomakerov9281.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include <cstdint> - -#include <thread> - -#include "constants.h" -#include "image.h" - -#include "icamera.h" - -class InnoMakerOV9281 : public ICamera -{ - constexpr static char videoDevice[] = "/dev/video0"; - -public: - using buffer_t = std::array<uint8_t, img_size>; - -public: - InnoMakerOV9281(); - ~InnoMakerOV9281(); - -public: - static std::vector<std::shared_ptr<ICamera>> search(); - -public: - bool startStream() override; - - bool init(); - - bool setExposureTimeUs(int value) override; - bool setGain(int value) override; - - bool setLaserLevel(int value) override; - - bool setSomething(int value) override; - - // bool getImage(Image &image); - bool getImage(size_t &image); - // Image &getImage(); - -public: - libcamera::Signal<std::shared_ptr<Pixels>> newPixels; - libcamera::Signal<std::shared_ptr<Image>> newImage; - -private: - bool setCamParam(unsigned int v4l2controlId, int value); - bool openCam(); - bool selectCam(int camIdx = 0); - bool initCam(); - -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 deleted file mode 100644 index 8f84255..0000000 --- a/src/camera/ov9281.cpp +++ /dev/null @@ -1,419 +0,0 @@ -#include "ov9281.h" - -#include <iostream> -#include <thread> - -#include <string.h> -#include <sys/mman.h> - -#include <libcamera/camera.h> -#include <libcamera/camera_manager.h> -#include <libcamera/control_ids.h> -#include <libcamera/framebuffer_allocator.h> -#include <libcamera/request.h> - -#include "image.h" -#include "macro.h" -#include "pixels.h" -#include "rotaryencoder.h" -#include "typedefs.h" - -#include <QElapsedTimer> - -QElapsedTimer timer; -size_t fpsCounter{0}; - -OV9281::OV9281(const std::shared_ptr<libcamera::Camera> &camera) - : INIT_FIELD(camera) -{ - timer.start(); - std::cout << __func__ << ":\tid: " << m_camera->id(); -} - -OV9281::~OV9281() -{ - for (auto &[fd, mem] : m_mappedBuffers) - { - munmap(mem.first, mem.second); - } - - m_camera->release(); -} - -bool OV9281::init() -{ - if (m_camera->acquire() != EXIT_SUCCESS) - { - std::cerr << __func__ << ": " << m_camera->id() - << ": cannot acquire camera." << std::endl; - - return false; - } - - m_config = m_camera->generateConfiguration({libcamera::StreamRole::Raw}); - - if (m_config->empty()) - { - std::cerr << __func__ << ": " << m_camera->id() - << ": cannot generate configuration" << std::endl; - - return false; - } - - // m_config->orientation = libcamera::Orientation::Rotate90; - - libcamera::StreamConfiguration &streamConfig = m_config->at(0); - - std::cout << "supported pixel formats:\n"; - - for (const auto &pixelFormat : streamConfig.formats().pixelformats()) - { - std::cout << "\t" << pixelFormat.toString() << std::endl; - - for (const auto &size : streamConfig.formats().sizes(pixelFormat)) - { - std::cout << "\t\t" << size.toString() << std::endl; - } - } - - streamConfig.pixelFormat = OV9281::pixelFormat; - streamConfig.bufferCount = OV9281::bufferCount; - - if (!validateConfig()) - { - return false; - } - - if (!applyConfig()) { - return false; - } - - return true; -} - -bool OV9281::validateConfig() -{ - using namespace libcamera; - - auto status = m_config->validate(); - - // WARNING: unsafe - - switch (status) - { - case CameraConfiguration::Status::Valid: { - std::cout << __func__ << ": " << "config is valid" << std::endl; - break; - } - case CameraConfiguration::Status::Adjusted: { - if (m_config->empty()) - { - std::cerr << __func__ << ": " << "config is adjusted, but empty" - << std::endl; - return false; - } - - libcamera::StreamConfiguration &streamConfig = m_config->at(0); - std::cout << __func__ - << ":\tpixelFormat: " << streamConfig.pixelFormat.toString() - << std::endl; - std::cout << __func__ << ":\tbufferCount: " << streamConfig.bufferCount - << std::endl; - // std::cout << __func__ << ":\torientation: " << m_config->orientation - // << std::endl; - break; - } - case CameraConfiguration::Status::Invalid: { - std::cerr << __func__ << ":\tconfig is invalid" << std::endl; - - return false; - } - } - - return true; -} - -bool OV9281::applyConfig() -{ - // FIXME: may crassh even on success (e.g. by setting pixelFormat to "8") - if (m_camera->configure(m_config.get()) != EXIT_SUCCESS) - { - std::cerr << __func__ << ":\tcannot apply config" << std::endl; - - return false; - } - - return true; -} - -/* - * Signals operate in the libcamera CameraManager thread context, so it is - * important not to block the thread for a long time, as this blocks internal - * processing of the camera pipelines, and can affect realtime performance. - */ -void OV9281::onRequestCompleted(libcamera::Request *completed_request) -{ - fpsCounter++; - - if (timer.elapsed() > 1000) { - std::cout << "fps: " << fpsCounter << std::endl; - timer.restart(); - fpsCounter = 0; - } - - using namespace libcamera; - - if (completed_request->status() == Request::RequestCancelled) - { - std::cerr << __func__ << ":\trequest canceled" << std::endl; - - return; - } - - const auto &buffers = completed_request->buffers(); - -#if 1 - for (auto [stream, buffer] : buffers) - { - const auto &streamConfig = stream->configuration(); - const auto &imageSize = streamConfig.size; - const auto &pixelFormat = streamConfig.pixelFormat; - const auto &stride = streamConfig.stride; - - const FrameMetadata &metadata = buffer->metadata(); - - for (size_t i = 0; i < buffer->planes().size(); ++i) - { - const FrameBuffer::Plane &plane = buffer->planes()[i]; - const FrameMetadata::Plane &metaplane = buffer->metadata() - .planes()[i]; - - size_t size = std::min(metaplane.bytesused, plane.length); - // std::cout << "size is: " << size << std::endl; - void *data = m_mappedBuffers[plane.fd.get()].first; - - auto img = std::make_shared<Image>(); - - img->width = imageSize.width; - img->height = imageSize.height; - - // img->dataSize = size; - // img->stride = stride; - img->pixelFormat = pixelFormat; - img->counters.measurementCounter = metadata.sequence; - img->counters.timestampUs = metadata.timestamp / 1000; - img->counters.encoderPosition = RotaryEncoder::instance()->position(); - - img->copyFromData(data, size); - // memcpy(img->data, data, size); - img->rotate(); - - auto pixels = img->pixels(); -// FIXME: backup emit value -#ifdef emit -#undef emit - if (!pixels) { - std::cerr << "emit empty pixels" << std::endl; - } - newImage.emit(img); - // newPixels.emit(pixels); -#define emit -#endif - } - } -#endif - const libcamera::ControlList &metadata = completed_request->metadata(); - const ControlInfoMap &control_map = m_camera->controls(); - // const ControlIdMap & ctrlIdMap = control_map.idmap(); - - auto frameDurationCtrl = control_map.find(&controls::FrameDurationLimits); - double fps = frameDurationCtrl == control_map.end() ? - std::numeric_limits<double>::quiet_NaN() : - (1e6 / frameDurationCtrl->second.min().get<int64_t>()); - - auto exp = metadata.get(controls::ExposureTime); - auto ag = metadata.get(controls::AnalogueGain); - auto ae = metadata.get(controls::AeEnable); - // auto br= metadata.get(controls::Brightness); - static auto lastControls = completed_request->controls(); - - completed_request->reuse(Request::ReuseBuffers); - // completed_request->controls().set(libcamera::controls::AeEnable, false); - // completed_request->controls() - // .set(libcamera::controls::draft ::NoiseReductionMode, - // libcamera::controls::draft ::NoiseReductionModeEnum :: - // NoiseReductionModeHighQuality); - - completed_request->controls().set(libcamera::controls::ExposureTime, - m_exposureTime); - - m_camera->queueRequest(completed_request); -} - -std::vector<std::shared_ptr<OV9281>> OV9281::search( - std::unique_ptr<libcamera::CameraManager> &manager) -{ - std::vector<std::shared_ptr<OV9281>> result; - - for (const auto &camera : manager->cameras()) - { - auto id = camera->id(); - auto c = manager->get(id); - // auto ov9281 = std::shared_ptr<OV9281>(new OV9281(c)); - - // if (!ov9281->init()) - // { - // continue; - // } - - // result.push_back(ov9281); - } - - return result; -} - -bool OV9281::startStream() -{ - if (m_config->empty()) - { - std::cerr << __func__ << ":\tconfig is empty" << std::endl; - return false; - } - - auto &streamConfig = m_config->at(0); - m_allocator = std::make_unique<libcamera::FrameBufferAllocator>(m_camera); - auto stream = streamConfig.stream(); - auto ret = m_allocator->allocate(stream); - - // TODO: check if zero - if (ret < 0) - { - std::cerr << __func__ << ":\tcan't allocate buffers: " << strerror(ret) - << std::endl; - return false; - } - - auto allocatedCount = ret; - std::cout << __func__ << ":\tallocated " << allocatedCount - << " buffers for stream" << std::endl; - - const auto &buffers = m_allocator->buffers(stream); - - for (const auto &buffer : buffers) - { - auto request = m_camera->createRequest(); - - if (!request) - { - std::cerr << __func__ << ":\tcan't create request" << std::endl; - - return false; - } - - // TODO: try multiple buffers per request and compare performance - ret = request->addBuffer(stream, buffer.get()); - - if (ret < 0) - { - std::cerr << __func__ - << ":\tcan't set buffer for request: " << strerror(ret) - << std::endl; - - return false; - } - - for (const auto &plane : buffer->planes()) - { - void *memory = mmap(NULL, - plane.length, - PROT_READ, - MAP_SHARED, - plane.fd.get(), - 0); - m_mappedBuffers[plane.fd.get()] = std::make_pair(memory, - plane.length); - } - - std::int64_t lowerUS = 1 * 1000 * 1000 / desiredFPS; - std::int64_t higherUS = lowerUS; - std::int64_t value_pair[2] = {higherUS / 2, higherUS}; - request->controls().set(libcamera::controls::AnalogueGain, 1.0); - request->controls().set(libcamera::controls::ExposureTime, 100); - request->controls().set(libcamera::controls::FrameDurationLimits, - libcamera::Span<const std::int64_t, 2>( - value_pair)); - - m_requests.push_back(std::move(request)); - } - - m_camera->requestCompleted.connect(this, &OV9281::onRequestCompleted); - - // FIXME: memleak - std::unique_ptr<libcamera::ControlList> camcontrols{ - new libcamera::ControlList()}; - - { - using namespace std::chrono_literals; - std::this_thread::sleep_for(500ms); - } - - ret = m_camera->start(camcontrols.get()); - - if (ret) - { - std::cerr << __func__ << ":\tfailed to start camera: " << strerror(ret) - << std::endl; - - return false; - } - - for (auto &request : m_requests) - { - ret = m_camera->queueRequest(request.get()); - - if (ret) - { - std::cerr << __func__ - << ":\tfailed to queue request: " << strerror(ret) - << std::endl; - - return false; - } - } - - return true; -} - -void OV9281::printControls() -{ - using namespace libcamera; - const libcamera::ControlInfoMap &control_map = m_camera->controls(); - - // for (const auto & [id, info]: control_map) - for (const std::pair<const ControlId *, ControlInfo> &pair : control_map) - { - const ControlId *const &id = pair.first; - const ControlInfo &info = pair.second; - - std::cout << "\tc " << id->name() << " (" << id->id() - << "): " << info.toString() - << (info.def().isNone() ? - "" : - " (dflt:" + info.def().toString() + ")"); - - if (!info.values().size()) - { - std::cout << std::endl; - continue; - } - - std::cout << " - ["; - - for (const auto &v : info.values()) - { - std::cout << " " << v.toString(); - } - - std::cout << " ]\n"; - } -} diff --git a/src/camera/ov9281.h b/src/camera/ov9281.h deleted file mode 100644 index e4b7417..0000000 --- a/src/camera/ov9281.h +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once - -#include <map> -#include <memory> -#include <vector> - -#include <libcamera/formats.h> - -#include <QObject> - -#include "icamera.h" - -namespace libcamera { -class Camera; -class CameraConfiguration; -class CameraManager; -class FrameBufferAllocator; -class Request; -} // namespace libcamera - -class Image; -class Pixels; - -class OV9281 : public QObject, public ICamera -{ - // Q_OBJECT - -public: - ~OV9281(); - -public: - static std::vector<std::shared_ptr<OV9281>> search( - std::unique_ptr<libcamera::CameraManager> &manager); - - // public functions -public: - bool startStream() override; - void printControls(); - - bool setExposureTimeUs(int value) override - { - assert(false); - return false; - }; - bool setGain(int value) override - { - assert(false); - return false; - }; - bool setLaserLevel(int value) override - { - assert(false); - return false; - }; - - // signals -public: - // TODO: image->pixels in separate thread - // TODO: respect sender/receiver threads - // libcamera::Signal<std::shared_ptr<Pixels>> newPixels; - // libcamera::Signal<std::shared_ptr<Image>> newImage; - -private: - explicit OV9281(const std::shared_ptr<libcamera::Camera> &camera); - - // private functions -private: - bool init(); - bool validateConfig(); - bool applyConfig(); - - void onRequestCompleted(libcamera::Request *completed_request); - - // constants -private: - static inline constexpr auto pixelFormat{libcamera::formats::R8}; - static inline constexpr unsigned int bufferCount{2}; - static inline constexpr size_t desiredFPS{144}; - - // member variables -private: - std::shared_ptr<libcamera::Camera> m_camera{nullptr}; - std::unique_ptr<libcamera::CameraConfiguration> m_config{nullptr}; - std::map<int, std::pair<void *, unsigned int>> m_mappedBuffers; - std::vector<std::unique_ptr<libcamera::Request>> m_requests; - std::unique_ptr<libcamera::FrameBufferAllocator> m_allocator{nullptr}; - - // TODO: set exposureTime from outside - int32_t m_exposureTime{3000}; -}; diff --git a/src/camera/veye_i2c.cpp b/src/camera/veye_i2c.cpp index c34da51..b9ad2fa 100644 --- a/src/camera/veye_i2c.cpp +++ b/src/camera/veye_i2c.cpp @@ -12,7 +12,9 @@ #include "veyeimx287m_types.h" -bool veye::imx287m::i2cRead(int fd, uint8_t i2c_addr, uint16_t reg, uint32_t &value) +std::optional<uint32_t> veye::imx287m::i2c_read(int fd, + uint8_t i2c_addr, + uint16_t reg) { int err; uint8_t bufin[2] = {reg >> 8, reg & 0xff}; @@ -42,56 +44,16 @@ bool veye::imx287m::i2cRead(int fd, uint8_t i2c_addr, uint16_t reg, uint32_t &va //printf("Read i2c addr %02X\n", i2c_addr); if (err != msgset.nmsgs) { std::cerr << "read i2c err " << err << std::endl; - return false; + return {}; } - value = ntohl(*(uint32_t *) bufout); - // fprintf(stderr, "addr %04x : value %08x \n", reg + i, value); + const auto result = ntohl(*(uint32_t *) bufout); + // fprintf(stderr, "addr %04x : value %08x \n", reg + i, result); - return true; + return result; } -bool veye::imx287m::test(uint32_t value) -{ - // const std::string i2cDevName{"/dev/i2c-10"}; - const std::string i2cDevName{"/dev/i2c-2"}; - constexpr uint32_t i2cDevAddr{0x3b}; - int fd = open(i2cDevName.c_str(), O_RDWR); - - if (!fd) { - std::cerr << "cannot open i2c device " << i2cDevName << ", error: " << strerror(errno) - << std::endl; - return false; - } - - if (ioctl(fd, I2C_SLAVE_FORCE, i2cDevAddr) < 0) { - std::cerr << "cannot set i2c slave. dev: " << i2cDevName << ", addr: " << i2cDevAddr - << ", error: " << strerror(errno) << std::endl; - - return false; - } - - uint32_t expTime{0}; - // if (!i2cRead(fd, i2cDevAddr, (uint16_t) veye::imx287m::Register::ME_Time, expTime)) { - // return false; - // } - - // std::cout << "exp time is: " << expTime << std::endl; - std::cout << "set exp time to " << value; - - if (!i2cWrite(fd, (uint16_t) veye::imx287m::Register::ME_Time, value)) { - return false; - } - - if (!i2cRead(fd, i2cDevAddr, (uint16_t) veye::imx287m::Register::ME_Time, expTime)) { - return false; - } - std::cout << "\texp time is: " << expTime << std::endl; - - return true; -} - -bool veye::imx287m::i2cWrite(int fd, uint16_t reg, const uint32_t value) +bool veye::imx287m::i2c_write(int fd, uint16_t reg, const uint32_t value) { uint8_t msg[] = {reg >> 8, reg & 0xff, value >> 24, value >> 16, value >> 8, value >> 0}; int len = sizeof(msg) / sizeof(msg[0]); @@ -120,8 +82,32 @@ veye::imx287m::i2c::~i2c() } } +std::optional<uint32_t> veye::imx287m::i2c::read(uint16_t reg) +{ + if (m_fd == -1 && !open()) { + return {}; + } + + return i2c_read(m_fd, m_address, reg); +} + +bool veye::imx287m::i2c::write(uint16_t reg, const uint32_t value) +{ + if (m_fd == -1 && !open()) { + return false; + } + + return i2c_write(m_fd, reg, value); +} + bool veye::imx287m::i2c::open() { + if (m_fd != -1) { + std::cout << __func__ << ": " << m_name + << " is already open, do nothing" << std::endl; + return true; + } + m_fd = ::open(m_name.c_str(), O_RDWR); if (!m_fd) { diff --git a/src/camera/veye_i2c.h b/src/camera/veye_i2c.h index 001d483..24ef10b 100644 --- a/src/camera/veye_i2c.h +++ b/src/camera/veye_i2c.h @@ -1,6 +1,7 @@ #pragma once #include <cstdint> +#include <optional> #include <string> namespace veye { @@ -12,20 +13,30 @@ public: ~i2c(); public: - bool read(uint16_t reg, uint32_t &value); - bool write(uint16_t reg, const uint32_t value); - -private: + /*! + * \brief open - open i2c device with `name` passed to constructor + * \return - true on success or if already open, false otherwise + */ bool open(); + std::optional<uint32_t> read(uint16_t reg); + bool write(uint16_t reg, const uint32_t value); + private: int m_fd{-1}; std::string m_name; int m_address{-1}; }; -bool i2cRead(int fd, uint8_t i2c_addr, uint16_t reg, uint32_t &value); -bool i2cWrite(int fd, uint16_t reg, const uint32_t value); -bool test(uint32_t value); +/*! + * \brief i2c_read - read device registers using i2c + * \param fd - device file descriptor + * \param i2c_addr - i2c adderr + * \param reg - register + * \param value - value to set + * \return non-empty register value on success, empty value on failure + */ +std::optional<uint32_t> i2c_read(int fd, uint8_t i2c_addr, uint16_t reg); +bool i2c_write(int fd, uint16_t reg, const uint32_t value); } // namespace imx287m } // namespace veye diff --git a/src/camera/veyeimx287m.cpp b/src/camera/veyeimx287m.cpp index b8fdbb4..a03aa83 100644 --- a/src/camera/veyeimx287m.cpp +++ b/src/camera/veyeimx287m.cpp @@ -9,20 +9,15 @@ #include <sys/mman.h> #include <unistd.h> -#include <execution> #include <iostream> -#include <ranges> -#include <span> #include <QElapsedTimer> +// orpheus #include "camera/veye_i2c.h" #include "constants.h" -#include "httpservice.h" -#include "imagealgos.h" -#include "mem_utils.h" -#include "pixels.h" -// #include "rotaryencoder.h" +#include "protocols/httpserver.h" +#include "veyeimx287m_types.h" static const struct v4l2_format_info { @@ -232,7 +227,9 @@ VeyeIMX287m::~VeyeIMX287m() if (m_cam_fd >= 0) { if (close(m_cam_fd) == -1) { - std::cout << __func__ << ": cannot close camera: " << strerror(errno) << std::endl; + std::cout << __func__ + << ": cannot close camera: " << strerror(errno) + << std::endl; } }; @@ -242,35 +239,26 @@ VeyeIMX287m::~VeyeIMX287m() std::vector<std::shared_ptr<ICamera> > VeyeIMX287m::search() { // return only one camera for now - std::cout << std::boolalpha; - auto cam = std::make_shared<VeyeIMX287m>(); + const auto cam = std::make_shared<VeyeIMX287m>(); if (!cam->init()) return {}; - if (!cam->setExposureTimeUs(30)) + // if (!cam->set_autoExposure(false)) + if (!cam->set_autoExposure(true)) return {}; - if (!cam->setLaserLevel(1)) + if (!cam->set_exposureTime(std::chrono::microseconds(30))) return {}; - if (!cam->setGain(2)) + if (!cam->set_autoGain(false)) return {}; - if (!cam->setSomething(0)) { + if (!cam->set_gain(0.1)) return {}; - } - // m_someThread = std::jthread{[=](std::stop_token stopToken) { - // std::cout << "VeyeIMX287m: start stream" << std::endl; - // sleep(5); - - // static int i = 0; - // while (!stopToken.stop_requested()) { - // cam->setSomething(i); - // i -= 1; - // } - // }}; + if (!cam->setLaserLevel(1)) + return {}; return {cam}; } @@ -283,8 +271,8 @@ bool VeyeIMX287m::startStream() auto ret = ioctl(m_cam_fd, VIDIOC_STREAMON, &radxa_buf_type); if (ret != 0) { - std::cerr << "ioctl(VIDIOC_STREAMON) failed: " << errno << " (" << strerror(errno) << ")" - << std::endl; + std::cerr << "ioctl(VIDIOC_STREAMON) failed: " << errno << " (" + << strerror(errno) << ")" << std::endl; return false; } @@ -310,123 +298,110 @@ bool VeyeIMX287m::init() if (!initCam()) return false; + if (!initI2C()) { + return false; + } + if (!initHttpServer()) return false; return true; } -bool VeyeIMX287m::setExposureTime(const std::chrono::microseconds us) +bool VeyeIMX287m::set_autoExposure(const bool enable) { - const std::string i2c_dev{"/dev/i2c-2"}; - const auto address = 0x3b; - const auto metime_reg = 0xc10; - const auto val = us.count(); - - const int fd = open(i2c_dev.c_str(), O_RDWR); + using namespace veye::imx287m; - if (!fd) { - std::cerr << "cannot open i2c dev: " << i2c_dev << ", errno: " << errno << " (" - << strerror(errno) << ")" << std::endl; - return false; - } + const uint32_t value = static_cast<uint32_t>( + enable ? ExposureMode::AutoExposureContinious : ExposureMode::Manual); - return true; + return m_i2c->write(static_cast<uint16_t>(Register::Exposure_Mode), value); } -bool VeyeIMX287m::setExposureTimeUs(int valueUs) +std::optional<bool> VeyeIMX287m::get_autoExposure() { - std::cout << "AAAAAAAAAAAAAAAAAAAAAAAAAAAAA: " << __func__ << ": " << valueUs << std::endl; - const std::string i2c_dev{"/dev/i2c-2"}; - const auto address = 0x3b; - const auto metime_reg = 0xc10; + using namespace veye::imx287m; - //return true; - std::cout << __func__ << ": " << V4L2_CID_EXPOSURE << " - " << valueUs << std::endl - << std::flush; + const auto value = m_i2c->read( + static_cast<uint32_t>(Register::Exposure_Mode)); - /* - * Shutter Time. Value is from 8721ns to 8721*885ns, must be integral - * multiple of 8721ns . - * 8721xN(N =1,2,3,4,5.....855) - */ - // constexpr int exposureStep{8721}; - // constexpr int maxExposureStepMultiplier{885}; - // auto valueNs = valueUs; - // valueNs = (valueNs / exposureStep) * exposureStep; + if (!value) { + return {}; + } - // std::clamp(valueNs, exposureStep, exposureStep * maxExposureStepMultiplier); - // setGain(rand() % 254); - // setGain(3); - // setLaserLevel(rand() % 0x7fffffff); - // setLaserLevel(rand() % 100); - // int exp = rand() % 10; - // return setCamParam(V4L2_CID_EXPOSURE, exp * exp * exp * exp * exp * exp); - // return setCamParam(V4L2_CID_EXPOSURE, valueUs); - return setCamParam(V4L2_CID_EXPOSURE, valueUs); + return *value + == static_cast<uint32_t>(ExposureMode::AutoExposureContinious); } -bool VeyeIMX287m::setGain(int value) +bool VeyeIMX287m::set_autoGain(const bool enable) { - std::cout << __func__ << ": " << value << std::endl << std::flush; + using namespace veye::imx287m; - // return setCamParam(V4L2_CID_GAIN, value); - // FIXME: tmp workaround for imx287llr - return true; + const uint32_t value = static_cast<uint32_t>( + enable ? GainMode::AutoGainContinious : GainMode::Manual); + + return m_i2c->write(static_cast<uint16_t>(Register::Gain_Mode), value); } -bool VeyeIMX287m::setLaserLevel(int value) +std::optional<bool> VeyeIMX287m::get_autoGain() { - std::cout << __func__ << ": " << value << std::endl << std::flush; + using namespace veye::imx287m; - // return setCamParam(V4L2_CID_FLASH_TIMEOUT, value); + const auto value = m_i2c->read(static_cast<uint32_t>(Register::Gain_Mode)); - // FIXME: tmp workaround for imx287llr - return true; + if (!value) { + return {}; + } + + return *value == static_cast<uint32_t>(GainMode::AutoGainContinious); } -bool VeyeIMX287m::setSomething(int value) +bool VeyeIMX287m::set_exposureTime(const std::chrono::microseconds us) { - std::cout << __func__ << ": " << value << std::endl << std::flush; - - // return setCamParam(V4L2_CID_FLASH_INTENSITY, value); - // FIXME: tmp workaround for imx287llr - return true; + using namespace veye::imx287m; + return m_i2c->write(static_cast<uint16_t>(Register::ME_Time), us.count()); } -bool VeyeIMX287m::setCamParam(unsigned int v4l2controlId, int value) +std::optional<const std::chrono::microseconds> VeyeIMX287m::get_exposureTime() { - veye::imx287m::test(value); + using namespace veye::imx287m; - std::cout << "radxa: skip setCamParam" << std::endl; + const auto value = m_i2c->read(static_cast<uint32_t>(Register::ME_Time)); - return true; - - v4l2_control ctl{v4l2controlId, value}; + if (!value) { + return {}; + } - if (ioctl(m_cam_fd, VIDIOC_S_CTRL, &ctl) < 0) { - fprintf(stderr, - "cannot set cam param: id - %d, error - '%s'\n", - v4l2controlId, - strerror(errno)); - fflush(stderr); + return std::chrono::microseconds{*value}; +} - return false; - } +bool VeyeIMX287m::set_gain(const float value) +{ + using namespace veye::imx287m; + return m_i2c->write(static_cast<uint16_t>(Register::Manual_Gain), + static_cast<uint32_t>(value * 10)); +} - if (ioctl(m_cam_fd, VIDIOC_G_CTRL, &ctl) < 0) { - fprintf(stderr, - "cannot get cam param: id - %d, error - '%s'\n", - v4l2controlId, - strerror(errno)); +std::optional<float> VeyeIMX287m::get_gain() +{ + using namespace veye::imx287m; - fflush(stderr); + const auto value = m_i2c->read(static_cast<uint32_t>(Register::Manual_Gain)); - return false; + if (!value) { + return {}; } - std::cout << __func__ << ": new value is " << ctl.value << std::endl; + return *value * 10; +} + +bool VeyeIMX287m::setLaserLevel(int value) +{ + std::cout << __func__ << ": " << value << std::endl << std::flush; + + // return setCamParam(V4L2_CID_FLASH_TIMEOUT, value); + // FIXME: tmp workaround for imx287llr return true; } @@ -435,7 +410,10 @@ bool VeyeIMX287m::openCam() m_cam_fd = open(videoDevice, O_RDWR); if (m_cam_fd < 0) { - fprintf(stderr, "cannot open cam '%s', error: '%s'\n", videoDevice, strerror(errno)); + fprintf(stderr, + "cannot open cam '%s', error: '%s'\n", + videoDevice, + strerror(errno)); return false; } @@ -448,7 +426,10 @@ bool VeyeIMX287m::selectCam(int camIdx) int ret = ioctl(m_cam_fd, VIDIOC_S_INPUT, &input); if (ret < 0) { - fprintf(stderr, "cannot select cam: idx - %d, error - '%s'\n", camIdx, strerror(errno)); + fprintf(stderr, + "cannot select cam: idx - %d, error - '%s'\n", + camIdx, + strerror(errno)); return false; } @@ -474,7 +455,9 @@ bool VeyeIMX287m::initCam() ret = ioctl(m_cam_fd, VIDIOC_TRY_FMT, &format); if (ret < 0) { - fprintf(stderr, "cannot try cam format: error - '%s'\n", strerror(errno)); + fprintf(stderr, + "cannot try cam format: error - '%s'\n", + strerror(errno)); return false; } @@ -485,7 +468,9 @@ bool VeyeIMX287m::initCam() ret = ioctl(m_cam_fd, VIDIOC_S_FMT, &format); if (ret < 0) { - fprintf(stderr, "cannot set cam format: error - '%s'\n", strerror(errno)); + fprintf(stderr, + "cannot set cam format: error - '%s'\n", + strerror(errno)); return false; } @@ -541,7 +526,9 @@ bool VeyeIMX287m::initCam() ret = ioctl(m_cam_fd, VIDIOC_REQBUFS, &rb); if (ret < 0) { - fprintf(stderr, "cannot set cam request buffers: ioctl error - '%s'\n", strerror(errno)); + fprintf(stderr, + "cannot set cam request buffers: ioctl error - '%s'\n", + strerror(errno)); return false; } @@ -565,7 +552,8 @@ bool VeyeIMX287m::initCam() std::cout << "query buffers" << std::endl; for (uint32_t i = 0; i < rb.count; i++) { - std::cout << "-----------------------------------------------------" << std::endl; + std::cout << "-----------------------------------------------------" + << std::endl; struct v4l2_buffer buf; struct v4l2_plane planes[VIDEO_MAX_PLANES]; @@ -583,8 +571,8 @@ bool VeyeIMX287m::initCam() if (ret < 0) { // std::cout << "ioctl(VIDIOC_QUERYBUF) failed: " << errno << " " << std::endl; - std::cerr << "ioctl(VIDIOC_QUERYBUF) failed: " << errno << " (" << strerror(errno) - << ")" << std::endl; + std::cerr << "ioctl(VIDIOC_QUERYBUF) failed: " << errno << " (" + << strerror(errno) << ")" << std::endl; return false; } @@ -597,9 +585,15 @@ bool VeyeIMX287m::initCam() const auto length = buf.m.planes[0].length; const auto offset = buf.m.planes[0].m.mem_offset; - buffers[i].mem[0] = mmap(0, length, PROT_READ | PROT_WRITE, MAP_SHARED, m_cam_fd, offset); + buffers[i].mem[0] = mmap(0, + length, + PROT_READ | PROT_WRITE, + MAP_SHARED, + m_cam_fd, + offset); if (buffers[i].mem[0] == MAP_FAILED) { - std::cerr << "mmap() failed: " << errno << " (" << strerror(errno) << ")" << std::endl; + std::cerr << "mmap() failed: " << errno << " (" << strerror(errno) + << ")" << std::endl; std::cerr << "length: " << length << std::endl; std::cerr << "offset: " << offset << std::endl; return false; @@ -616,15 +610,18 @@ bool VeyeIMX287m::initCam() buffers[i].size[i] = length; buffers[i].padding[i] = 0; - printf("Buffer %u/%u mapped at address %p.\n", buffers[i].idx, i, buffers[i].mem[0]); + printf("Buffer %u/%u mapped at address %p.\n", + buffers[i].idx, + i, + buffers[i].mem[0]); // buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // buf.memory = V4L2_MEMORY_MMAP; // buf.index = i; ret = ioctl(m_cam_fd, VIDIOC_QBUF, &buf); if (ret != 0) { - std::cerr << "ioctl(VIDIOC_QBUF) failed: " << errno << " (" << strerror(errno) << ")" - << std::endl; + std::cerr << "ioctl(VIDIOC_QBUF) failed: " << errno << " (" + << strerror(errno) << ")" << std::endl; return false; } @@ -650,6 +647,13 @@ bool VeyeIMX287m::initCam() return true; } +bool VeyeIMX287m::initI2C() +{ + m_i2c = std::make_shared<veye::imx287m::i2c>(); + + return m_i2c != nullptr && m_i2c->open(); +} + bool VeyeIMX287m::initHttpServer() { m_httpServer = std::make_shared<HttpServer>(this); @@ -761,8 +765,8 @@ bool VeyeIMX287m::dequeueImageBuffer(size_t &imageIndex) } if (ret != 0) { - std::cerr << "ioctl(VIDIOC_DQBUF) failed: " << errno << " (" << strerror(errno) << ")" - << std::endl; + std::cerr << "ioctl(VIDIOC_DQBUF) failed: " << errno << " (" + << strerror(errno) << ")" << std::endl; return false; } @@ -780,11 +784,13 @@ bool VeyeIMX287m::dequeueImageBuffer(size_t &imageIndex) // image.counters.encoderPosition = RotaryEncoder::instance()->position(); image.counters.measurementCounter = buf.sequence; + // FIXME: git rid of static vars static int64_t prevCounter = buf.sequence; dropped_count += buf.sequence - prevCounter - 1; prevCounter = buf.sequence; - image.counters.timestampUs = buf.timestamp.tv_sec * 1000 * 1000 + buf.timestamp.tv_usec; + image.counters.timestampUs = buf.timestamp.tv_sec * 1000 * 1000 + + buf.timestamp.tv_usec; { std::lock_guard<std::mutex> lock(m_camMtx); @@ -792,8 +798,8 @@ bool VeyeIMX287m::dequeueImageBuffer(size_t &imageIndex) } if (ret != 0) { - std::cerr << "ioctl(VIDIOC_QBUF) failed: " << errno << " (" << strerror(errno) << ")" - << std::endl; + std::cerr << "ioctl(VIDIOC_QBUF) failed: " << errno << " (" + << strerror(errno) << ")" << std::endl; return false; } diff --git a/src/camera/veyeimx287m.h b/src/camera/veyeimx287m.h index 7d7e2c6..d8c42ae 100644 --- a/src/camera/veyeimx287m.h +++ b/src/camera/veyeimx287m.h @@ -10,6 +10,12 @@ #include "icamera.h" +namespace veye { +namespace imx287m { +class i2c; +} // namespace imx287m +} // namespace veye + class HttpServer; class VeyeIMX287m : public ICamera @@ -29,18 +35,26 @@ public: public: bool startStream() override; + bool dequeueImageBuffer(size_t &image); + bool getImage(Image &image); + bool init(); - bool setExposureTime(const std::chrono::microseconds us); - bool setExposureTimeUs(int value) override; - bool setGain(int value) override; + // parameters +public: + bool set_autoExposure(const bool enable) override; + std::optional<bool> get_autoExposure() override; - bool setLaserLevel(int value) override; + bool set_autoGain(const bool enable) override; + std::optional<bool> get_autoGain() override; - bool setSomething(int value) override; + bool set_exposureTime(const std::chrono::microseconds us) override; + std::optional<const std::chrono::microseconds> get_exposureTime() override; - bool dequeueImageBuffer(size_t &image); - bool getImage(Image &image); + bool set_gain(const float value) override; + std::optional<float> get_gain() override; + + bool setLaserLevel(int value) override; public: /*! @@ -50,10 +64,11 @@ public: uint32_t processedCounter{0}; private: - bool setCamParam(unsigned int v4l2controlId, int value); bool openCam(); bool selectCam(int camIdx = 0); bool initCam(); + + bool initI2C(); bool initHttpServer(); void calcFrameLoop(std::stop_token stopToken); @@ -95,5 +110,6 @@ private: // std::jthread m_calcThreads[1]; std::jthread m_calcThreads[4]; + std::shared_ptr<veye::imx287m::i2c> m_i2c; std::shared_ptr<HttpServer> m_httpServer; }; diff --git a/src/camera/veyeimx287m_types.h b/src/camera/veyeimx287m_types.h index 640a0e1..8cb74ba 100644 --- a/src/camera/veyeimx287m_types.h +++ b/src/camera/veyeimx287m_types.h @@ -5,7 +5,7 @@ namespace veye { namespace imx287m { -enum class Register { +enum class Register : uint16_t { ImageAcqu = 0x0400, ImageFeature = 0x0800, ImageSigProc = 0x0c00, @@ -126,6 +126,18 @@ enum class Register { GPIO2_Reverse = 0x1038 }; +enum class ExposureMode : uint32_t { + Manual = 0, + AutoExposureOnce = 1, + AutoExposureContinious = 2 +}; + +enum class GainMode : uint32_t { + Manual = 0, + AutoGainOnce = 1, + AutoGainContinious = 2 +}; + class Params : public QObject { // Q_OBJECT diff --git a/src/iscanner.cpp b/src/iscanner.cpp new file mode 100644 index 0000000..87eab9a --- /dev/null +++ b/src/iscanner.cpp @@ -0,0 +1,5 @@ +#include "iscanner.h" + +IScanner::IScanner(std::shared_ptr<ICamera> camera) + : m_camera{camera} +{} diff --git a/src/iscanner.h b/src/iscanner.h new file mode 100644 index 0000000..e01f7b5 --- /dev/null +++ b/src/iscanner.h @@ -0,0 +1,19 @@ +#pragma once + +// cpp +#include <memory> +#include <vector> + +class IProtocol; +class ICamera; + +class IScanner +{ +public: + explicit IScanner(std::shared_ptr<ICamera> camera); + virtual ~IScanner() = default; + +protected: + std::shared_ptr<ICamera> m_camera; + std::vector<std::shared_ptr<IProtocol>> m_protocols; +}; diff --git a/src/macro.h b/src/macro.h index d03482a..070fd4d 100644 --- a/src/macro.h +++ b/src/macro.h @@ -17,5 +17,3 @@ { \ name \ } - -#define INNO_MAKER diff --git a/src/main.cpp b/src/main.cpp index 60e4668..5a7d701 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,17 +13,15 @@ #include "LibCamera.h" #include "calibration.h" -#include "camera/innomakerov9281.h" -#include "camera/ov9281.h" #include "camera/veye_i2c.h" #include "camera/veyeimx287m.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 "protocols/httpserver.h" // #include "pigpio.h" #include "printerclient.h" #include "profile.h" @@ -179,34 +177,6 @@ int main(int argc, char* argv[]) QList<QFuture<void>> initializers; -#ifdef INNO_MAKER - if (false) - { - std::cout << std::boolalpha; - InnoMakerOV9281 innoMakerCam; - qDebug() << "init:" << innoMakerCam.init(); - // qDebug() << "set exposure:" << innoMakerCam.setExposureTimeUs(3000000); - qDebug() << "set exposure:" << innoMakerCam.setExposureTimeUs(10000); - 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 (false) { @@ -400,11 +370,6 @@ int main(int argc, char* argv[]) qDebug() << "msecs before camera:" << t.elapsed(); // FIXME: don't use one var for everything int ret; -#ifndef INNO_MAKER - std::unique_ptr<libcamera::CameraManager> cm = - std::make_unique<libcamera::CameraManager>(); - cm->start(); -#endif // const auto cameras = cm->cameras(); // const auto cameras = OV9281::search(cm); // const auto cameras = InnoMakerOV9281::search(); @@ -414,21 +379,14 @@ int main(int argc, char* argv[]) 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->newPixels.connect(&onNewPixels); // camera->newImage.connect(&onNewImage); // camera->newImageCallback = &onNewImage; camera->newPixelsCallback = &onNewPixels; @@ -439,10 +397,6 @@ int main(int argc, char* argv[]) if (!camera->startStream()) { -#ifndef INNO_MAKER - cm->stop(); -#endif - return EXIT_FAILURE; } @@ -634,7 +588,8 @@ int main(int argc, char* argv[]) // qDebug() << "set new exposure time:" << value; // requested_params.exposureTime = value; - if (!camera->setExposureTimeUs(value)) { + if (!camera->set_exposureTime( + std::chrono::microseconds(value))) { qDebug() << "cannot set exp"; return QHttpServerResponse::StatusCode:: RequestRangeNotSatisfiable; @@ -643,13 +598,13 @@ int main(int argc, char* argv[]) if (json.contains(gainKey)) { - const int32_t value{json[gainKey].toInt()}; + const auto value = json[gainKey].toDouble(); if (value == 0) return QHttpServerResponse::StatusCode:: RequestRangeNotSatisfiable; - if (!camera->setGain(value)) + if (!camera->set_gain(value)) return QHttpServerResponse::StatusCode:: RequestRangeNotSatisfiable; } @@ -683,10 +638,6 @@ int main(int argc, char* argv[]) auto result = app.exec(); -#ifndef INNO_MAKER - cm->stop(); -#endif - return result; } diff --git a/src/httpservice.cpp b/src/protocols/httpserver.cpp index efcf17f..41226aa 100644 --- a/src/httpservice.cpp +++ b/src/protocols/httpserver.cpp @@ -1,7 +1,10 @@ -#include "httpservice.h" +#include "httpserver.h" // qt #include <QHttpServer> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonValue> // orpheus #include "camera/veyeimx287m.h" @@ -23,8 +26,7 @@ HttpServer::HttpServer(ICamera *camera, // QObject *parent, const QHostAddress &address, const uint16_t port) - // : QObject{parent} - : INIT_FIELD(camera) + : ProtocolBase{camera} , INIT_FIELD(address) , INIT_FIELD(port) , m_server{std::make_shared<QHttpServer>()} @@ -32,7 +34,16 @@ HttpServer::HttpServer(ICamera *camera, const auto apiPrefix = QStringLiteral("/v1"); const auto pixelsPath = apiPrefix + "/pixels"; qDebug().noquote() << Q_FUNC_INFO << ": pixelsPath: " << pixelsPath; + m_server->route(pixelsPath, [this]() { return GET_pixels(); }); + m_server->route(apiPrefix + QStringLiteral("/sensor/params"), + QHttpServerRequest::Method::Get, + [this]() { return GET_params(); }); + m_server->route(apiPrefix + QStringLiteral("/sensor/params"), + QHttpServerRequest::Method::Post, + [this](const QHttpServerRequest &request) { + return POST_params(request); + }); qDebug().noquote() << Q_FUNC_INFO << ": listen: " << m_server->listen(m_address, m_port); } @@ -124,3 +135,85 @@ QHttpServerResponse HttpServer::GET_pixels() return QHttpServerResponse{res}; } + +QHttpServerResponse HttpServer::POST_params(const QHttpServerRequest &request) +{ + const auto json = QJsonDocument::fromJson(request.body()).object(); + qDebug().noquote() << __func__ << ": " << json; + + const auto jsonError = [](const auto message) -> const QString { + return QJsonDocument{QJsonObject{{QStringLiteral("error"), message}}} + .toJson(); + }; + const auto invalidValue = + [jsonError](const auto fieldName) -> const QString { + return jsonError(fieldName + QStringLiteral(" has invalid value")); + }; + + const auto autoExposure = json["autoExposure"]; + const auto exposureTime = json["exposureTime"]; + const auto autoGain = json["autoGain"]; + const auto gain = json["gain"]; + + if (!autoExposure.isNull()) { + if (autoExposure.isBool()) { + m_camera->set_autoExposure(autoExposure.toBool()); + } else { + return {invalidValue("autoExposure"), + QHttpServerResponse::StatusCode::BadRequest}; + } + } + + if (!exposureTime.isNull()) { + if (exposureTime.isDouble()) { + m_camera->set_exposureTime( + std::chrono::microseconds{exposureTime.toInt()}); + } else { + return {invalidValue("exposureTime"), + QHttpServerResponse::StatusCode::BadRequest}; + } + } + + if (!autoGain.isNull()) { + if (autoGain.isBool()) { + m_camera->set_autoGain(autoGain.toBool()); + } else { + return {invalidValue("autoGain"), + QHttpServerResponse::StatusCode::BadRequest}; + } + } + + if (!gain.isNull()) { + if (gain.isDouble()) { + m_camera->set_gain(gain.toDouble()); + } else { + return {invalidValue("gain"), + QHttpServerResponse::StatusCode::BadRequest}; + } + } + + return QHttpServerResponse::StatusCode::Ok; +} + +QHttpServerResponse HttpServer::GET_params() +{ + const auto val_or_null = [](const auto &optional) -> QJsonValue { + return optional ? QJsonValue{*optional} : QJsonValue::Null; + }; + + const auto chrono_val_or_null = [](const auto &optional) -> QJsonValue { + return optional ? static_cast<qint64>(optional->count()) + : QJsonValue::Null; + }; + + const auto json = QJsonObject{ + {"autoExposure", val_or_null(m_camera->get_autoExposure())}, + {"exposureTime", chrono_val_or_null(m_camera->get_exposureTime())}, + {"autoGain", val_or_null(m_camera->get_autoGain())}, + {"gain", val_or_null(m_camera->get_gain())}, + }; + + qDebug().noquote() << __func__ << ": " << json; + + return QHttpServerResponse{QJsonDocument{json}.toJson()}; +} diff --git a/src/httpservice.h b/src/protocols/httpserver.h index 0fbd54d..86b6ad6 100644 --- a/src/httpservice.h +++ b/src/protocols/httpserver.h @@ -5,10 +5,13 @@ #include <QHttpServerResponse> // #include <QObject> +// orpheus +#include "iprotocol.h" + class ICamera; class QHttpServer; -class HttpServer // : public QObject +class HttpServer : public ProtocolBase { // Q_OBJECT @@ -27,14 +30,16 @@ public: // QObject *parent = nullptr, const QHostAddress &address = DefaultAddress, const uint16_t port = DefaultPort); + ~HttpServer() override = default; // TODO: methods starting with GET_/POST_ will be routed automatically public: QHttpServerResponse GET_pixels(); + QHttpServerResponse POST_params(const QHttpServerRequest &request); + QHttpServerResponse GET_params(); + private: - // std::weak_ptr<ICamera> m_camera; - ICamera *m_camera{nullptr}; QHostAddress m_address{DefaultAddress}; uint16_t m_port{DefaultPort}; diff --git a/src/protocols/iprotocol.cpp b/src/protocols/iprotocol.cpp new file mode 100644 index 0000000..f85d990 --- /dev/null +++ b/src/protocols/iprotocol.cpp @@ -0,0 +1,5 @@ +#include "iprotocol.h" + +ProtocolBase::ProtocolBase(ICamera *camera) + : m_camera{camera} +{} diff --git a/src/protocols/iprotocol.h b/src/protocols/iprotocol.h new file mode 100644 index 0000000..3827550 --- /dev/null +++ b/src/protocols/iprotocol.h @@ -0,0 +1,19 @@ +#pragma once + +class ICamera; + +class IProtocol +{ +public: + virtual ~IProtocol() = default; +}; + +class ProtocolBase : public IProtocol +{ +public: + explicit ProtocolBase(ICamera *camera); + ~ProtocolBase() override = default; + +protected: + ICamera *m_camera{nullptr}; +}; diff --git a/src/protocols/protocolbase.cpp b/src/protocols/protocolbase.cpp new file mode 100644 index 0000000..9116d25 --- /dev/null +++ b/src/protocols/protocolbase.cpp @@ -0,0 +1,3 @@ +#include "protocolbase.h" + +ProtocolBase::ProtocolBase() {} diff --git a/src/protocols/protocolbase.h b/src/protocols/protocolbase.h new file mode 100644 index 0000000..fb41dca --- /dev/null +++ b/src/protocols/protocolbase.h @@ -0,0 +1,10 @@ +#ifndef PROTOCOLBASE_H +#define PROTOCOLBASE_H + +class ProtocolBase +{ +public: + ProtocolBase(); +}; + +#endif // PROTOCOLBASE_H |
