diff options
| -rw-r--r-- | CMakeLists.txt | 16 | ||||
| -rw-r--r-- | imagealgos.cpp | 14 | ||||
| -rw-r--r-- | imagealgos.h | 24 | ||||
| -rw-r--r-- | main.cpp | 487 | ||||
| -rw-r--r-- | printerclient.cpp | 74 | ||||
| -rw-r--r-- | printerclient.h | 29 | ||||
| -rw-r--r-- | rotaryencoder.cpp | 24 | ||||
| -rw-r--r-- | rotaryencoder.h | 14 |
8 files changed, 491 insertions, 191 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index ccbc957..83ef86c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,10 @@ set(CMAKE_CXX_STANDARD 23) cmake_minimum_required(VERSION 3.18) include_guard(GLOBAL) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR arm) @@ -19,6 +23,16 @@ set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT}) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${TARGET_SYSROOT}/usr/include") set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}") +# NOTE: I've added ld symlink on host to be able to run moc +# sudo ln -s /usr/aarch64-linux-gnu/lib/ld-linux-aarch64.so.1 /lib/ld-linux-aarch64.so.1 +# +# TODO: check how to add this env var to cmake, it's needed to run moc, +# otherwise you'll get the following error +# +# ...../rpi-sysroot/usr/lib/qt6/libexec/moc: error while loading shared libraries: +# libpcre2-16.so.0: cannot open shared object file: No such file or directory +# +# LD_LIBRARY_PATH=/home/nikita/rpi/rpi-sysroot/usr/lib/aarch64-linux-gnu/:$LD_LIBRARY_PATH set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) @@ -66,6 +80,8 @@ qt_add_executable(apporpheus genetic_algos.cpp rotaryencoder.h rotaryencoder.cpp + printerclient.h + printerclient.cpp ) target_link_libraries(app${PROJECT_NAME} PRIVATE diff --git a/imagealgos.cpp b/imagealgos.cpp index e22f1bd..3e84155 100644 --- a/imagealgos.cpp +++ b/imagealgos.cpp @@ -31,10 +31,11 @@ size_t pgm_save(Image *img, FILE *outfile, bool really_save) { 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) + for (size_t i = 0; i < img_width * img_height; ++i) { uint16_t *pixels = (uint16_t*)img->data; - const auto p = pixels[i]; + // const auto p = pixels[i]; uint8_t value = (pixels[i] & 0xFF00) >> 8; // n += fwrite(&value, 1, 1, outfile); @@ -249,15 +250,18 @@ float process_column(uint16_t (&column)[]) #endif } -void process_columns(Image &image) +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]); - image.pixels[i] = process_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; @@ -268,4 +272,6 @@ void process_columns(Image &image) } stop_timer(process_columns); + + return result; } diff --git a/imagealgos.h b/imagealgos.h index bf5611a..77ac625 100644 --- a/imagealgos.h +++ b/imagealgos.h @@ -1,5 +1,6 @@ #pragma once +#include <array> #include <cstddef> #include <cstdio> @@ -9,6 +10,13 @@ constexpr size_t img_width = 1280; constexpr size_t img_height = 800; constexpr uint32_t patternSize = 16; +struct Counters +{ + uint32_t timestampUs { 0 }; + uint32_t measurementCounter { 0 }; + int32_t encoderPosition { 0 }; +}; + struct Image { int width; @@ -18,9 +26,17 @@ struct Image size_t dataSize; unsigned int stride; libcamera::PixelFormat pixelFormat; - float pixels[img_width]; - unsigned int measurementCounter; - uint64_t timestampNs; + // float pixels[img_width]; + Counters counters {}; + // unsigned int measurementCounter; + // uint32_t timestampUs; + // int32_t encoderPosition; +}; + +struct Pixels +{ + Counters counters {}; + std::array<float, img_width> pixels { 0.f }; }; size_t pgm_save(Image *img, FILE *outfile, bool really_save = false); @@ -35,4 +51,4 @@ T median3(const T& a, const T& b, const T& c) { } void rotate(Image & image); -void process_columns(Image & image); +Pixels process_columns(Image & image); @@ -1,6 +1,16 @@ + +#include "httpservice.h" +#include "genetic_algos.h" +#include "imagealgos.h" +#include "LibCamera.h" +#include "pigpio.h" +#include "printerclient.h" +#include "rotaryencoder.h" + #define QT_NO_KEYWORDS #include <QCoreApplication> #include <QDebug> +#include <QDir> #include <QFile> #include <QHttpServer> #include <QJsonArray> @@ -19,13 +29,6 @@ #include <string.h> #include <thread> -#include "httpservice.h" -#include "genetic_algos.h" -#include "imagealgos.h" -#include "LibCamera.h" -#include "pigpio.h" -#include "rotaryencoder.h" - #define try_apply_config() \ if(!applyConfig(config)) \ { \ @@ -39,13 +42,37 @@ if(!applyConfig(config)) \ const QString exposureTimeKey = "exposureTime"; const QString laserLevelKey = "laserLevel"; -extern volatile int64_t positionSteps; +enum ScanningModeFlags : uint8_t { + None = 0, + Calibration +}; + +// ScanningModeFlags operator|(ScanningModeFlags lhs, ScanningModeFlags rhs) +// { +// using T = std::underlying_type<ScanningModeFlags>::type; +// return static_cast<ScanningModeFlags>(static_cast<T>(lhs) | static_cast<T>(rhs)); +// } + +ScanningModeFlags scanningModeFlags { ScanningModeFlags::None }; + +QElapsedTimer calibrationTimer; + +extern volatile int32_t positionSteps; struct requested_params_t { - int32_t exposureTime = { 200 }; - int32_t laserLevel = { 7000 }; + int32_t exposureTime = { 1000 }; + int32_t laserLevel = { 3000 }; + uint32_t stepsPerMm { 200 }; } requested_params; + +namespace { Image img; +Pixels pixels; +std::vector<Pixels> calibrationPixels; +QMutex calibrationPixelsMutex; +} + +const QString dumpsRoot { QStringLiteral("/home/user/dumps") }; using namespace std::chrono_literals; @@ -58,6 +85,8 @@ ControlList lastControls; static bool applyConfig(const std::unique_ptr<CameraConfiguration> & config); static void onRequestCompleted(Request *completed_request); static void printControls(); +static void dumpCalibrationPixels(); +static std::vector<Pixels> openDump(const QString dumpPath = ""); int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); @@ -65,47 +94,10 @@ int main(int argc, char *argv[]) { QElapsedTimer t; t.start(); - // qDebug() << "msecs before serial:" << t.elapsed(); - - // QSerialPort *serialPort { new QSerialPort { "/dev/ttyUSB0", qApp } }; - - // if (!serialPort->setBaudRate(QSerialPort::Baud115200)) { - // qDebug() << "serial port: cannot set baud rate:" - // << serialPort->errorString(); - - // return EXIT_FAILURE; - // } - - // if (!serialPort->open(QFile::ReadWrite)) { - // qDebug() << "cannot open serial port:" << serialPort->errorString(); - - // return EXIT_FAILURE; - // } - - // qDebug() << "serial port baud rate:" << serialPort->baudRate(); - - // qDebug() << "serial port data bits:" << serialPort->dataBits(); - // qDebug() << "serial port parity:" << serialPort->parity(); - // qDebug() << "serial port stop bits:" << serialPort->stopBits(); - - // QObject::connect(serialPort, &QSerialPort::readyRead, - // serialPort, [=](){ - // qDebug() << "serialPort: " << serialPort->readAll(); - // }); - - // QObject::connect(serialPort, &QSerialPort::errorOccurred, - // serialPort, [=](){ - // qWarning() << "serial port error:" << serialPort->errorString(); - // }); - - // qDebug() << "msecs before write and flush:" << t.elapsed(); - - // serialPort->write(QByteArray { "G91\n" }); - // serialPort->flush(); - qDebug() << "msecs before encoder:" << t.elapsed(); - rotaryencoder encoder; + RotaryEncoder encoder; + PrinterClient printerClient; qDebug() << "msecs before camera:" << t.elapsed(); // FIXME: don't use one var for everything @@ -115,6 +107,8 @@ int main(int argc, char *argv[]) { const auto cameras = cm->cameras(); + openDump(); + if (cameras.empty()) { std::cout << "No cameras were identified on the system." << std::endl; @@ -270,16 +264,16 @@ int main(int argc, char *argv[]) { std::make_pair(memory, plane.length); } - // size_t desiredFPS = 144; + size_t desiredFPS = 144; - // std::int64_t lowerUS = 1 * 1000 * 1000 / desiredFPS; - // std::int64_t higherUS = lowerUS; - // std::int64_t value_pair[2] = { higherUS / 2, higherUS }; + 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)); + request->controls().set( + libcamera::controls::FrameDurationLimits, + libcamera::Span<const std::int64_t, 2>(value_pair)); requests.push_back(std::move(request)); } @@ -333,14 +327,15 @@ int main(int argc, char *argv[]) { QJsonArray pixels; for (size_t i = 0; i < img_width; ++i) { - pixels << img_height - img.pixels[i]; + // pixels << img_height - img.pixels[i]; + pixels << img_height - ::pixels.pixels[i]; } QJsonObject json; json["pixels"] = pixels; json["encoderPosition"] = qint64 { encoder.position() }; - json["measurementCounter"] = qint64 { img.measurementCounter }; - json["timestampUs"] = qint64 { img.timestampNs / 1000. } ; + json["measurementCounter"] = qint64 { img.counters.measurementCounter }; + json["timestampUs"] = qint64(img.counters.timestampUs); return QHttpServerResponse(QJsonDocument(json).toJson()); }); @@ -358,6 +353,48 @@ int main(int argc, char *argv[]) { return QHttpServerResponse::StatusCode::Ok; }); + qHttpServer.route("/v1/commands/startCalibration", [&](const QHttpServerRequest &request) -> QHttpServerResponse { + 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 { + 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 { + 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 { switch (request.method()) { @@ -621,77 +658,65 @@ void onRequestCompleted(Request *completed_request) size_t size = std::min(metaplane.bytesused, plane.length); void * data = mappedBuffers_[plane.fd.get()].first; - // std::cout << metaplane.bytesused << "/" << plane.length; - - // std::cout << " stride " << stride; - // std::cout << " planes count: " << buffer->planes().size() << " "; - // std::cout << std::endl; - - // if (metadata.sequence == 20) - { - // FIXME: remove hardcode - img.width = imageSize.width; - img.height = imageSize.height; - // img.data = data; - memcpy(img.data, data, size); - img.dataSize = size; - img.stride = stride; - img.pixelFormat = pixelFormat; - img.measurementCounter = metadata.sequence; - img.timestampNs = metadata.timestamp; - - // uint16_t unpacked[img.width * img.height] = { 0 }; - // unpack_16bit((uint8_t*)img.data, img, (uint16_t*)&unpacked); - // img.data = unpacked; - // img.dataSize = img.width * img.height * sizeof(uint16_t); - rotate(img); - process_columns(img); - // static bool done = false; - // mark pixels and max region - // for (size_t i = 0; i < img_width; ++i) - // { - // // std::cout << "\t" << img.pixels[i] << std::endl; - // // uint - // // const auto & p = img.pixels[i]; - // // const auto int_p = int(p); - // // const auto fract = p - int_p; + // FIXME: remove hardcode + img.width = imageSize.width; + img.height = imageSize.height; + // img.data = data; + memcpy(img.data, data, size); + 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(); + // qDebug() << "pos:" << img.counters.encoderPosition; + // uint16_t unpacked[img.width * img.height] = { 0 }; + // unpack_16bit((uint8_t*)img.data, img, (uint16_t*)&unpacked); + // img.data = unpacked; + // img.dataSize = img.width * img.height * sizeof(uint16_t); + rotate(img); + Pixels pixels = process_columns(img); + ::pixels = pixels; - // // img.data[int_p][i] = 256 * 256 * fract; - // // img.data[int_p + 1][i] = 256 * 256 * (1.0 - fract); + // qDebug() << "calibration mode" << scanningModeFlags; + if (scanningModeFlags == ScanningModeFlags::Calibration) { + constexpr int32_t hardcodedZRangeMm { 175 }; + const int32_t maxEncoderPosition = hardcodedZRangeMm * requested_params.stepsPerMm; + // qDebug() << "calibration max range" << maxEncoderPosition; + // qDebug() << "calibration encoder pos" << pixels.counters.encoderPosition; + if (pixels.counters.encoderPosition >= 0 && + pixels.counters.encoderPosition <= maxEncoderPosition) + { + qDebug() << "calibration save at pos:" << pixels.counters.encoderPosition; + QMutexLocker l(&calibrationPixelsMutex); + ::calibrationPixels.push_back(std::move(pixels)); + } + else if (pixels.counters.encoderPosition > maxEncoderPosition) + { + // save to files + QMutexLocker l(&calibrationPixelsMutex); + qDebug() << "calibration pixels count:" + << ::calibrationPixels.size(); + qDebug() << "calibration elapsed (s):" + << calibrationTimer.elapsed() / 1000; + // ::calibrationPixels.clear(); + // TODO: use flags + // qDebug() << "stop calibration mode"; + scanningModeFlags = ScanningModeFlags::None; - // // if (!done) { - // // std::cout << fract << " "; - // // } - - // img.data[size_t(img.pixels[i])][i] = 0; - // // crash is possible - // img.data[size_t(img.pixels[i]) - patternSize / 2][i] = 0xffff; - // img.data[size_t(img.pixels[i]) + patternSize / 2][i] = 0xffff; - // } - // done = true; - - // // FILE * f = fopen("/tmp/R16.pgm", "w"); - // FILE * f = fopen("/tmp/img.pgm", "w"); - // // // FILE * f = fopen("/tmp/MONO_PISP_COMP1.pgm", "w"); - - // if (f == NULL) - // { - // std::cerr << "cannot open output file: " - // << strerror(errno) - // << std::endl; - // } - // else - // { - // // pgm_save(&img, f); - // // pgm_save(&img, f); - // fclose(f); - // // std::cout << "file written" << std::endl; - // } - pgm_save(&img, nullptr); + QFuture<void> dumpCalirationPixelsFuture = + QtConcurrent::run(&dumpCalibrationPixels); + } + else + { + // qDebug() << "calibration skip at pos:" << pixels.counters.encoderPosition; + } } + + pgm_save(&img, nullptr); } } @@ -723,63 +748,17 @@ void onRequestCompleted(Request *completed_request) << std::endl; } - // for (const auto & [id, value] : metadata) - // { - - // } - - // metadata.set(controls::ExposureTime, 300); - - // exp->set(*exp + 1); - // expTimeCtrl->second().set(*exp + 1); - // auto expTimeCtrlId= expTimeCtrl->id(); - - - // properties.set(controls::ExposureTime, 1000); - - // std::optional<uint32_t> expTimeOptional = properties.get(controls::ExposureTime); - - // if (expTimeOptional.has_value()) - // { - // // uint32_t value = expTimeOptional.value(); - - // auto frameDurationLimits = controls.find(&controls::FrameDurationLimits)->second; - // auto min = frameDurationLimits.min().get<int64_t>(); - // auto max = frameDurationLimits.max().get<int64_t>(); - // // auto val = properties.find(controls::FrameDurationLimits)->value();//.second().min().get<int64_t>() - // // auto second = val.second(); - // auto framerate = 1.0e6 / min; - // auto rAG = request->controls().get<float>(libcamera::controls::AnalogueGain); - // auto rET = request->controls().get<int32_t>(libcamera::controls::ExposureTime); - // int32_t randET = rand() % 9000 + 1000; - // request->controls().set(libcamera::controls::ExposureTime, 100); - // std::cout << "exposure time (us): " - // << properties.get(controls::ExposureTime).value() - // << " frame duration limits (ns): " << min << "/" << max - // << " framerate: " << framerate - // << " " << rAG.value_or(321) - // << " " << rET.value_or(321) - // << std::endl; - // // std::cout << "noise reduction mode: " - // // << properties.get(controls::AwbMode).value() - // // << std::endl; - // // NoiseReductionMode - // } - - // completed_request->reuse(Request::ReuseBuffers); - // camera->queueRequest(completed_request); - - qDebug() << "set exposureTime to" << requested_params.exposureTime; + 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, requested_params.exposureTime); - 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); - camera->queueRequest(completed_request); ++performanceCounter; @@ -869,3 +848,157 @@ static void printControls() std::cout << " ]\n"; } } + +static void dumpCalibrationPixels() +{ + std::vector<Pixels> rawProfiles; + + { + QMutexLocker l(&calibrationPixelsMutex); + std::swap(rawProfiles, ::calibrationPixels); + } + + const QString dumpSubdir { + QDateTime::currentDateTime().toString("yyyy.MM.dd_hh.mm.ss") + }; + const QDir dumpPath { dumpsRoot + "/" + dumpSubdir }; + + if (!dumpPath.mkdir(dumpPath.path())) + { + qWarning() << "cannot create dump dir: " << dumpPath.path(); + + return; + } + + for (const auto& rawProfile : rawProfiles) + { + const auto filename = QLatin1String("raw_profile_meas_%1_enc_%2") + .arg(QString::number(rawProfile.counters.measurementCounter)) + .arg(rawProfile.counters.encoderPosition); + const auto filepath = dumpPath.path() + "/" + filename; + + QFile f { filepath }; + + if (!f.open(QFile::WriteOnly)) + { + qWarning() << "cannot open dump dump file" << f.fileName(); + qWarning() << "error is:" << f.errorString(); + + return; + } + + QJsonObject jsonCounters { + { "timestampUs", qint64(rawProfile.counters.timestampUs) }, + { "measurementCounter", qint64(rawProfile.counters.measurementCounter) }, + { "encoderPosition", qint64(rawProfile.counters.encoderPosition) }, + }; + + QJsonObject json; + + json["counters"] = jsonCounters; + + QJsonArray jsonPixels; + + for (const auto& pixel : rawProfile.pixels) + { + jsonPixels << pixel; + } + + json["pixels"] = jsonPixels; + + if (!f.write(QJsonDocument(json).toJson())) + { + qWarning() << "cannot write file" << f.fileName(); + qWarning() << "error is" << f.errorString(); + + return; + } + + qDebug() << "file written: " << f.fileName(); + } + + qDebug() << "dump finished"; +} + +static std::vector<Pixels> openDump(const QString dumpPath) +{ + std::vector<Pixels> result; + + QString dirToRead { dumpPath }; + + if (dirToRead.isEmpty()) + { + qDebug() << "dumpPath not specified. looking into" << dumpsRoot; + + QDir dumpsRootDir { dumpsRoot }; + + const auto filter = QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable; + // there is no battery in my rpi5 for now + const auto sort = QDir::Name; + const auto entries = dumpsRootDir.entryList(filter, sort); + + if (entries.isEmpty()) + { + qWarning() << "dumps root" << dumpsRoot << "contains no dumps. " + << "specify existing dump path"; + + return {}; + } + + dirToRead = entries.last(); + } + + QDir dumpDir { dumpsRoot + "/" + dirToRead }; + + const auto filter = QDir::Files; + const auto sort = QDir::Name; + + const auto filenames = dumpDir.entryList(filter, sort); + + if (filenames.isEmpty()) + { + qDebug() << "no filenames found in" << dumpDir.path(); + } + + for (const auto& filename : filenames) + { + qDebug() << "raw profile:" << filename; + + QFile f { dumpDir.path() + "/" + filename }; + + if (!f.open(QFile::ReadOnly)) + { + qWarning() << "cannot open file for reading:" << f.fileName(); + qWarning() << "error string:" << f.errorString(); + + return {}; + } + + // TODO: rewrite to remove manual serialization/deserialization + const auto json = QJsonDocument::fromJson(f.readAll()).object(); + + const auto jsonCounters = json["counters"].toObject(); + qDebug() << jsonCounters; + + Pixels rawProfile; + + rawProfile.counters.timestampUs = jsonCounters["timestampUs"].toInteger(); + rawProfile.counters.measurementCounter = jsonCounters["measurementCounter"].toInteger(); + rawProfile.counters.encoderPosition = jsonCounters["encoderPosition"].toInteger(); + + const auto jsonPixels = json["pixels"].toArray(); + qDebug() << jsonPixels.count() << rawProfile.pixels.size(); + + for (size_t i = 0; i < jsonPixels.count() && i < rawProfile.pixels.size(); ++i) + { + rawProfile.pixels[i] = jsonPixels[i].toDouble(); + } + } + + // { + // QMutexLocker l(&calibrationPixelsMutex); + // std::swap(result, ::calibrationPixels); + // } + + return result; +} diff --git a/printerclient.cpp b/printerclient.cpp new file mode 100644 index 0000000..c5bf189 --- /dev/null +++ b/printerclient.cpp @@ -0,0 +1,74 @@ +#include "printerclient.h" + +#include <exception> + +#include <QDebug> +#include <QFile> +#include <QSerialPort> + +PrinterClient::PrinterClient(QObject *parent) + : QObject { parent } + , m_serialPort { new QSerialPort { "/dev/ttyUSB0", 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 new file mode 100644 index 0000000..5f4f2a3 --- /dev/null +++ b/printerclient.h @@ -0,0 +1,29 @@ +#pragma once + +#include <QObject> + +#include <QSerialPort> + +class QSerialPort; + +class PrinterClient : public QObject +{ + Q_OBJECT + +public: + explicit PrinterClient(QObject *parent = nullptr); + ~PrinterClient() override = default; + +signals: + void newData(const QString output); + +public slots: + void sendCommand(const QString command); + +private slots: + void onReadyRead(); + void onErrorOccured(QSerialPort::SerialPortError error); + +private: + QSerialPort* m_serialPort { nullptr }; +}; diff --git a/rotaryencoder.cpp b/rotaryencoder.cpp index 41d56e8..62ca33d 100644 --- a/rotaryencoder.cpp +++ b/rotaryencoder.cpp @@ -26,7 +26,7 @@ const std::vector<int> encoderTable = { 0, 1, -1, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, -1, 1, 0 }; -volatile int64_t positionSteps; +volatile int32_t positionSteps; volatile uint8_t state; void pin_isr(void) { @@ -55,8 +55,14 @@ void pin_isr(void) { } } -rotaryencoder::rotaryencoder() +RotaryEncoder::RotaryEncoder() { + if (!m_self) { + m_self = this; + } else { + qWarning() << "normally there should be only one instance of RotaryEncoder"; + } + QElapsedTimer t; t.start(); @@ -101,7 +107,19 @@ rotaryencoder::rotaryencoder() // } } -int64_t rotaryencoder::position() const +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 index f2f9b76..c0c39d8 100644 --- a/rotaryencoder.h +++ b/rotaryencoder.h @@ -2,11 +2,19 @@ #include <cstdint> -class rotaryencoder +// TODO: delete singleton functionality +class RotaryEncoder final { public: - rotaryencoder(); + RotaryEncoder(); + ~RotaryEncoder(); public: - int64_t position() const; + static RotaryEncoder* instance(); + +public: + int32_t position() const; + +private: + static inline RotaryEncoder* m_self { nullptr }; }; |
