From 1f65ba01144a67b3f652ef75a90943afa20a3ab5 Mon Sep 17 00:00:00 2001 From: lenn Date: Fri, 24 Oct 2025 17:15:53 +0800 Subject: [PATCH] feat: integrate tactile stream decoding --- CMakeLists.txt | 187 +-- README.md | 93 ++ components/charts/heatmap.cc | 31 +- components/charts/heatmap.hh | 22 +- components/charts/heatmap.impl.hh | 1 - components/comment/cpdecoder.cc | 226 ---- components/comment/cpdecoder.hh | 141 --- components/comment/tactile/tacdec.h | 56 - components/comment/tactile/tecdec.cc | 297 ----- components/ffmsep/cpdecoder.cc | 54 +- components/ffmsep/cpdecoder.hh | 58 +- components/ffmsep/cpstream_core.cc | 627 ++++++++++ components/ffmsep/cpstream_core.hh | 77 ++ .../ffmsep/tactile/{tecdec.cc => tacdec.cc} | 95 +- .../ffmsep/tactile/{tacdec.h => tacdec.hh} | 33 +- components/view.cc | 15 +- dlog/dlog.cc | 40 + dlog/dlog.hh | 81 ++ examples/cpstream_demo.cc | 120 ++ modern-qt/utility/painter-resource.hh | 1 - serial/CMakeLists.txt | 51 - serial/examples/serial_example.cc | 182 --- serial/include/serial/impl/unix.h | 221 ---- serial/include/serial/impl/win.h | 207 ---- serial/include/serial/serial.h | 775 ------------ serial/include/serial/v8stdint.h | 57 - .../src/impl/list_ports/list_ports_linux.cc | 336 ----- serial/src/impl/list_ports/list_ports_osx.cc | 286 ----- serial/src/impl/list_ports/list_ports_win.cc | 152 --- serial/src/impl/unix.cc | 1084 ----------------- serial/src/impl/win.cc | 646 ---------- serial/src/serial.cc | 432 ------- 32 files changed, 1284 insertions(+), 5400 deletions(-) create mode 100644 README.md delete mode 100644 components/comment/cpdecoder.cc delete mode 100644 components/comment/cpdecoder.hh delete mode 100644 components/comment/tactile/tacdec.h delete mode 100644 components/comment/tactile/tecdec.cc create mode 100644 components/ffmsep/cpstream_core.cc create mode 100644 components/ffmsep/cpstream_core.hh rename components/ffmsep/tactile/{tecdec.cc => tacdec.cc} (82%) rename components/ffmsep/tactile/{tacdec.h => tacdec.hh} (60%) create mode 100644 dlog/dlog.cc create mode 100644 dlog/dlog.hh create mode 100644 examples/cpstream_demo.cc delete mode 100644 serial/CMakeLists.txt delete mode 100644 serial/examples/serial_example.cc delete mode 100644 serial/include/serial/impl/unix.h delete mode 100644 serial/include/serial/impl/win.h delete mode 100644 serial/include/serial/serial.h delete mode 100644 serial/include/serial/v8stdint.h delete mode 100644 serial/src/impl/list_ports/list_ports_linux.cc delete mode 100644 serial/src/impl/list_ports/list_ports_osx.cc delete mode 100644 serial/src/impl/list_ports/list_ports_win.cc delete mode 100644 serial/src/impl/unix.cc delete mode 100644 serial/src/impl/win.cc delete mode 100644 serial/src/serial.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index a30b498..6a6d5b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,94 +1,129 @@ cmake_minimum_required(VERSION 3.20) project(touchsensor VERSION 2.0.0 LANGUAGES CXX) -set(BUILD_EXAMPLE ON) +option(BUILD_EXAMPLE "Build the cpstream demo executable" ON) +set(QT_VERSION Qt6 CACHE STRING "Qt major version to use") -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_BUILD_TYPE "Release") set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) add_compile_options(-Os -O3) -add_subdirectory("serial") -set(QT_VERSION Qt6) +list(APPEND CMAKE_PREFIX_PATH + "D:/Environment/include" + "D:/Environment/lib" +) + find_package(${QT_VERSION} REQUIRED COMPONENTS Widgets Network PrintSupport) find_package(Eigen3 REQUIRED) -include_directories(.) - -file( - GLOB_RECURSE PROJECT_SOURCE - CONFIGURE_DEPENDS - # Project source - "modern-qt/*.cc" - # Custom signals - "modern-qt/widget/sliders.hh" -) -add_library( - modern-qt SHARED - ${PROJECT_SOURCE} - modern-qt/widget/select.hh - modern-qt/widget/select.impl.hh -) -target_link_libraries( - modern-qt PUBLIC - ${QT_VERSION}::Widgets - ${QT_VERSION}::Network -) - -file( - GLOB_RECURSE WIDGETS_CC - CONFIGURE_DEPENDS "components/*.cc" -) - -file( - GLOB_RECURSE QCUSTOMPLOT_SOURCE - CONFIGURE_DEPENDS - "qcustomplot/*.cpp" - "qcustomplot/*.h" -) -add_library( - qcustomplot SHARED - ${QCUSTOMPLOT_SOURCE} -) -target_link_libraries( - qcustomplot PUBLIC - ${QT_VERSION}::Core - ${QT_VERSION}::Gui - ${QT_VERSION}::PrintSupport -) - qt_standard_project_setup() -add_executable( - ${PROJECT_NAME} - ${WIDGETS_CC} - main.cc - component.hh - resources.qrc - components/view.cc - modern-qt/widget/select.cc - components/charts/heatmap.cc - components/charts/heatmap.hh - components/charts/heatmap.impl.hh - dlog/dlog.hh - dlog/dlog.cc - components/ffmsep/cpdecoder.hh - components/ffmsep/cpdecoder.cc - components/ffmsep/tactile/tacdec.h - components/ffmsep/tactile/tecdec.cc + +file( + GLOB_RECURSE MODERN_QT_SOURCES + CONFIGURE_DEPENDS + "modern-qt/*.cc" ) -qt6_add_resources(QRC_FILES resources.qrc) -target_sources(${PROJECT_NAME} PRIVATE ${QRC_FILES}) -target_link_libraries( - ${PROJECT_NAME} - ${QT_VERSION}::Widgets - ${QT_VERSION}::Network - modern-qt - qcustomplot - serial - spdlog +set(MODERN_QT_HEADERS + modern-qt/widget/select.hh + modern-qt/widget/select.impl.hh + modern-qt/widget/sliders.hh ) +add_library(modern-qt SHARED ${MODERN_QT_SOURCES} ${MODERN_QT_HEADERS}) +target_include_directories(modern-qt PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(modern-qt + PUBLIC + ${QT_VERSION}::Widgets + ${QT_VERSION}::Network + Eigen3::Eigen +) + +file( + GLOB_RECURSE QCUSTOMPLOT_SOURCES + CONFIGURE_DEPENDS + "qcustomplot/*.cpp" + "qcustomplot/*.h" +) +add_library(qcustomplot SHARED ${QCUSTOMPLOT_SOURCES}) +target_include_directories(qcustomplot PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(qcustomplot + PUBLIC + ${QT_VERSION}::Core + ${QT_VERSION}::Gui + ${QT_VERSION}::PrintSupport +) + +file( + GLOB_RECURSE COMPONENT_SOURCES + CONFIGURE_DEPENDS + "components/*.cc" +) +file( + GLOB_RECURSE UTILITY_SOURCES + CONFIGURE_DEPENDS + "dlog/*.cc" +) + +set(FFMSEP_SOURCES + components/ffmsep/cpdecoder.cc + components/ffmsep/cpstream_core.cc + components/ffmsep/tactile/tacdec.cc +) +set(FFMSEP_HEADERS + components/ffmsep/cpdecoder.hh + components/ffmsep/cpstream_core.hh + components/ffmsep/tactile/tacdec.hh +) +set(FFMSEP_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/components/ffmsep") + +set(TOUCHSENSOR_HEADERS + component.hh + components/charts/heatmap.hh + components/charts/heatmap.impl.hh + dlog/dlog.hh + ${FFMSEP_HEADERS} +) + +qt6_add_resources(APP_RESOURCES resources.qrc) + +add_executable(${PROJECT_NAME} + ${COMPONENT_SOURCES} + ${UTILITY_SOURCES} + ${TOUCHSENSOR_HEADERS} + main.cc +) +target_sources(${PROJECT_NAME} PRIVATE ${APP_RESOURCES}) +target_include_directories(${PROJECT_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${FFMSEP_INCLUDE_DIR} +) +target_link_libraries(${PROJECT_NAME} + PRIVATE + ${QT_VERSION}::Widgets + ${QT_VERSION}::Network + modern-qt + qcustomplot + serial + setupapi + spdlog +) + +if(BUILD_EXAMPLE) + add_executable(cpstream_demo + examples/cpstream_demo.cc + ${FFMSEP_SOURCES} + ) + target_include_directories(cpstream_demo + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${FFMSEP_INCLUDE_DIR} + ) + target_link_libraries(cpstream_demo PRIVATE serial) + target_link_libraries(cpstream_demo PRIVATE setupapi) +endif() diff --git a/README.md b/README.md new file mode 100644 index 0000000..a85cd81 --- /dev/null +++ b/README.md @@ -0,0 +1,93 @@ +# TouchSensor 2.0 + +> Real-time tactile sensor exploration UI powered by Qt 6 and a custom Modern Qt component toolkit. + +![Status](https://img.shields.io/badge/status-prototype-orange?style=for-the-badge) +![Qt 6](https://img.shields.io/badge/Qt-6.5%2B-41CD52?style=for-the-badge&logo=qt) +![C++23](https://img.shields.io/badge/C%2B%2B-23-00599C?style=for-the-badge&logo=cplusplus) +![CMake](https://img.shields.io/badge/CMake-3.20%2B-064F8C?style=for-the-badge&logo=cmake) +![UI Toolkit](https://img.shields.io/badge/Modern%20Qt-internal-8A2BE2?style=for-the-badge) + +![TouchSensor Logo](images/logo.png) + +## Highlights +- Modern, material-inspired desktop shell built on the in-repo `modern-qt` library. +- Modular component system (`NavComponent`, `ViewComponent`) for quick UI experiments. +- Real-time tactile matrix visualisation via the `HeatMapPlot` wrapper around QCustomPlot. +- Codec infrastructure (`components/ffmsep`) ready for custom tactile packet decoding. +- Serial transport module scaffolded for COM port discovery and streaming. + +## Architecture At A Glance +- **Entry point**: `main.cc` composes the themed window, navigation rail, and card-based layout. +- **Components**: `components/` hosts UI widgets, charts, and the tactile decoder pipeline. +- **Modern Qt toolkit**: `modern-qt/` provides declarative wrappers, theming, and Material icon helpers. +- **Data layer**: `components/ffmsep` implements codec registration, packet decoding, and tactile frame processing. +- **Visualisation**: `components/charts/heatmap.*` exposes a themable heatmap control for sensor grids. + +## Getting Started + +### Prerequisites +- CMake 3.20+ +- A C++23-capable compiler (MSVC 19.3x, Clang 16+, or GCC 13+) +- Qt 6 (Widgets, Network, PrintSupport modules) +- Eigen3 +- `spdlog` (fetched via package manager or provided to CMake) + +Ensure `Qt6_DIR` (or `CMAKE_PREFIX_PATH`) points to the Qt install so CMake can locate the required modules. + +### Configure & Build + +![](https://picgo-upload.cn-nb1.rains3.com/2025/10/407c29f139a834ed0f79a81347f810f1.png) +```powershell +pacman -Sy +pacman -S mingw-w64-x86_64-toolchain +pacman -S mingw64/mingw-w64-x86_64-qt6-base +pacman -S mingw-w64-x86_64-eigen3 mingw-w64-x86_64-yaml-cpp + +mkdir build +cmake -G "MinGW Makefiles" -B build -DCMAKE_INSTALL_PREFIX="YOUR INSTALL PATH" + +cd build && mingw32-make install + +cat install_manifest.txt +``` + +To run from the build directory: + +```powershell +.\touchsensor.exe +``` + +On Linux/macOS adjust the Qt path and executable name accordingly. + +## Project Layout + +```text +. +|-- components/ +| |-- charts/ # QCustomPlot-based visualisations (heatmaps, etc.) +| |-- ffmsep/ # Codec system and tactile decoder experiments +| |-- view.cc # Main dashboard composition +| `-- ... # Additional UI widgets +|-- modern-qt/ # In-house declarative Qt UI framework +|-- serial/ # Serial communication helper library +|-- images/logo.png # Current app branding +|-- main.cc # Application bootstrap +`-- CMakeLists.txt # Build script (adds Qt, Eigen, Modern Qt, Serial, SPDLOG) +``` + +## Roadmap (WIP) +- Flesh out tactile codec implementations and connect them to live serial streams. +- Replace placeholder random data with decoded sensor frames. +- Expand navigation targets beyond the current demo cards. +- Capture screenshots or recordings for documentation. +- Polish theming, animation masks, and landing experience. + +## Contributing +This repository is in active development; feel free to open issues or PRs once guidelines land. Until then, keep discussions in the project chat or issues board. + +## Acknowledgements +- [Qt](https://www.qt.io/) for the core UI framework. +- [QCustomPlot](https://www.qcustomplot.com/) powering the heatmap widget. +- [spdlog](https://github.com/gabime/spdlog) for logging (wired via CMake). +- Internal **Modern Qt** toolkit built on top of creeper-qt utilities. diff --git a/components/charts/heatmap.cc b/components/charts/heatmap.cc index fa03122..edabc30 100644 --- a/components/charts/heatmap.cc +++ b/components/charts/heatmap.cc @@ -5,12 +5,8 @@ #include "heatmap.hh" #include "heatmap.impl.hh" #include "qcustomplot/qcustomplot.h" -#include -#include #include -#include #include -#include #include using namespace creeper::plot_widget::internal; @@ -21,32 +17,30 @@ BasicPlot::BasicPlot() : pimpl{std::make_unique(*this)} { setBackground(Qt::transparent); } -BasicPlot::~BasicPlot() {} +BasicPlot::~BasicPlot() = default; -void BasicPlot::set_matrix_size(const QSize& size) { - qDebug() << "set matrix size" << size; +void BasicPlot::set_matrix_size(const QSize& size)const { pimpl->set_matrix_size(size); } -void BasicPlot::set_matrix_size(const int& w, const int& h) { +void BasicPlot::set_matrix_size(const int& w, const int& h)const { QSize size(w, h); - qDebug() << "set matrix size" << size; pimpl->set_matrix_size(size); } -void BasicPlot::set_color_gradient_range(const double& min, const double& max) { +void BasicPlot::set_color_gradient_range(const double& min, const double& max)const { pimpl->set_color_gradient_range(min, max); } -void BasicPlot::set_xlabel_text(const QString& text) { +void BasicPlot::set_xlabel_text(const QString& text)const { pimpl->set_xlabel_text(text); } -void BasicPlot::set_ylabel_text(const QString& text) { +void BasicPlot::set_ylabel_text(const QString& text)const { pimpl->set_ylabel_text(text); } -void BasicPlot::load_theme_manager(ThemeManager& mgr) { +void BasicPlot::load_theme_manager(ThemeManager& mgr)const { pimpl->load_theme_manager(mgr); } @@ -54,8 +48,7 @@ QSize BasicPlot::get_matrix_size() const { return pimpl->get_matrix_size(); } -void BasicPlot::set_data(const QVector& data) { - qDebug() << "set data" << data.size(); +void BasicPlot::set_data(const QVector& data)const { pimpl->set_data(data); } @@ -63,7 +56,7 @@ bool BasicPlot::is_initialized() const { return pimpl->is_plot_initialized(); } -void BasicPlot::init_plot() { +void BasicPlot::init_plot()const { pimpl->initialize_plot(); } @@ -75,17 +68,17 @@ void BasicPlot::paintEvent(QPaintEvent* event) { QCustomPlot::paintEvent(event); } -void BasicPlot::dataChanged(const QVector& map) { +void BasicPlot::dataChanged(const QVector& map)const { set_data(map); // emit dataChanged(map); } -void BasicPlot::dataRangeChanged(const double& min, const double& max) { +void BasicPlot::dataRangeChanged(const double& min, const double& max)const { set_color_gradient_range(min, max); // emit dataRangeChanged(min, max); } -void BasicPlot::update_dynamic_heatmap(const QVector& map) { +void BasicPlot::update_dynamic_heatmap(const QVector& map)const { set_data(map); } diff --git a/components/charts/heatmap.hh b/components/charts/heatmap.hh index c8ae84c..c24a5e5 100644 --- a/components/charts/heatmap.hh +++ b/components/charts/heatmap.hh @@ -35,21 +35,21 @@ public: // BasicPlot(const BasicPlot&) = delete; // BasicPlot& operator=(const BasicPlot&) = delete; - void init_plot(); - void load_theme_manager(ThemeManager&); - void set_xlabel_text(const QString&); - void set_ylabel_text(const QString&); - void set_matrix_size(const QSize&); - void set_matrix_size(const int& w, const int& h); - void set_data(const QVector& data); - void set_color_gradient_range(const double& min, const double& max); + void init_plot()const; + void load_theme_manager(ThemeManager&)const; + void set_xlabel_text(const QString&)const; + void set_ylabel_text(const QString&)const; + void set_matrix_size(const QSize&)const; + void set_matrix_size(const int& w, const int& h)const; + void set_data(const QVector& data)const; + void set_color_gradient_range(const double& min, const double& max)const; QSize get_matrix_size() const; bool is_initialized() const; public slots: - void update_dynamic_heatmap(const QVector& map); - void dataChanged(const QVector& map); - void dataRangeChanged(const double& min, const double& max); + void update_dynamic_heatmap(const QVector& map)const; + void dataChanged(const QVector& map)const; + void dataRangeChanged(const double& min, const double& max)const; protected: void paintEvent(QPaintEvent*) override; diff --git a/components/charts/heatmap.impl.hh b/components/charts/heatmap.impl.hh index de066d6..f13e910 100644 --- a/components/charts/heatmap.impl.hh +++ b/components/charts/heatmap.impl.hh @@ -75,7 +75,6 @@ public: // 创建颜色映射 QCPColorMap* cpmp = new QCPColorMap(self.xAxis, self.yAxis); - qDebug() << "initialize_plot() size:" << matrix_size; cpmp->data()->setSize(matrix_size.width(), matrix_size.height()); cpmp->data()->setRange(QCPRange(0.5, matrix_size.width() - 0.5), QCPRange(0.5, matrix_size.height() - 0.5)); diff --git a/components/comment/cpdecoder.cc b/components/comment/cpdecoder.cc deleted file mode 100644 index a67405c..0000000 --- a/components/comment/cpdecoder.cc +++ /dev/null @@ -1,226 +0,0 @@ -// -// FFmpeg 风格编解码器注册表与上下文管理实现(中文注释版)。 -// 注意:此实现大量使用 std::vector、std::optional 等现代 C++ 容器与 RAII 思想。 -// - -#include "cpdecoder.hh" - -#include // 提供 std::find_if 等算法,用于查找与遍历 -#include // std::mutex + std::lock_guard,保证注册表线程安全 - -namespace ffmsep { - -namespace { // 匿名命名空间:限制辅助函数和静态变量作用域在当前编译单元 - -std::vector& codec_registry() { - static std::vector registry; // 使用静态局部变量 + vector 保留所有注册的编解码器 - return registry; -} - -std::mutex& registry_mutex() { - static std::mutex m; // 构建一个全局互斥量,用于守护注册表操作 - return m; -} - -void attach_codec(CPCodecContext* ctx, const CPCodec* codec) { - if (!ctx) { - return; - } - - ctx->codec = codec; - if (!codec) { - ctx->codec_type = CPMediaType::Unknown; - ctx->priv_data = nullptr; - ctx->release_priv_storage(); - return; - } - - ctx->codec_type = codec->type; - ctx->priv_data = ctx->ensure_priv_storage(codec->priv_data_size); -} - -bool codec_name_equals(const CPCodec* codec, std::string_view name) { - if (!codec || !codec->name) { - return false; - } - return std::string_view(codec->name) == name; // std::string_view 避免额外拷贝 -} - -} // namespace - -void* CPCodecContext::ensure_priv_storage(std::size_t size) { - // 将 vector 当作动态缓冲区,确保大小满足编解码器私有数据需求 - if (size == 0U) { - priv_storage.clear(); - priv_data = nullptr; - return nullptr; - } - if (priv_storage.size() != size) { - priv_storage.assign(size, static_cast(0)); - } - priv_data = priv_storage.data(); - return priv_data; -} - -void CPCodecContext::release_priv_storage() noexcept { - // 与 ensure 配套,释放时直接清空 vector - priv_storage.clear(); - priv_data = nullptr; -} - -// 将单个编解码器指针注册到全局列表,重复注册时作安全检查。 -void cpcodec_register(const CPCodec* codec) { - if (!codec || !codec->name) { - return; - } - - std::lock_guard lock(registry_mutex()); - auto& reg = codec_registry(); - auto already = std::find(reg.begin(), reg.end(), codec); - if (already != reg.end()) { - return; - } - - auto same_id = std::find_if(reg.begin(), reg.end(), [codec](const CPCodec* entry) { - return entry && codec && entry->id == codec->id && codec->id != CPCodecID::Unknown; - }); - if (same_id != reg.end()) { - *same_id = codec; // 如果 ID 已存在,则替换为新实现,以最新注册者为准 - return; - } - - reg.push_back(codec); -} - -// 批量注册函数:利用 C++11 std::initializer_list 便捷传入多个指针。 -void cpcodec_register_many(std::initializer_list codecs) { - for (const CPCodec* codec : codecs) { - cpcodec_register(codec); - } -} - -const CPCodec* cpcodec_find_decoder(CPCodecID id) { - std::lock_guard lock(registry_mutex()); - const auto& reg = codec_registry(); - auto it = std::find_if(reg.begin(), reg.end(), [id](const CPCodec* codec) { - return codec && codec->id == id; - }); - return it == reg.end() ? nullptr : *it; // 使用可空返回值:找不到时返回 nullptr -} - -const CPCodec* cpcodec_find_decoder_by_name(std::string_view name) { - std::lock_guard lock(registry_mutex()); - const auto& reg = codec_registry(); - auto it = std::find_if(reg.begin(), reg.end(), [name](const CPCodec* codec) { - return codec_name_equals(codec, name); - }); - return it == reg.end() ? nullptr : *it; -} - -std::vector cpcodec_list_codecs() { - std::lock_guard lock(registry_mutex()); - return codec_registry(); // 返回当前注册表的浅拷贝 -} - -// 分配一个新的上下文,并可选绑定已有编解码器。 -CPCodecContext* cpcodec_alloc_context3(const CPCodec* codec) { - auto* ctx = new CPCodecContext(); - if (codec) { - attach_codec(ctx, codec); - } - return ctx; -} - -// 打开上下文:允许调用方在此时指定(或替换)具体编解码器。 -int cpcodec_open2(CPCodecContext* ctx, const CPCodec* codec) { - if (!ctx) { - return CP_ERROR_INVALID_ARGUMENT; - } - - if (ctx->is_open) { - return CP_ERROR_INVALID_STATE; - } - - if (codec) { - attach_codec(ctx, codec); - } - - if (!ctx->codec) { - return CP_ERROR_INVALID_ARGUMENT; - } - - ctx->is_open = true; - if (ctx->codec->init) { - int rc = ctx->codec->init(ctx); - if (rc < 0) { - ctx->is_open = false; - if (ctx->codec->close) { - ctx->codec->close(ctx); // 初始化失败时调用 close 进行善后 - } - return rc; - } - } - return CP_SUCCESS; -} - -// 关闭上下文:调用编解码器收尾逻辑并释放私有状态。 -int cpcodec_close(CPCodecContext* ctx) { - if (!ctx) { - return CP_ERROR_INVALID_ARGUMENT; - } - - if (!ctx->is_open) { - return CP_SUCCESS; - } - - if (ctx->codec && ctx->codec->close) { - ctx->codec->close(ctx); - } - - ctx->is_open = false; - ctx->release_priv_storage(); - ctx->codec_type = CPMediaType::Unknown; - ctx->codec = nullptr; - ctx->priv_data = nullptr; - return CP_SUCCESS; -} - -// 释放上下文指针,防止悬挂引用。 -void cpcodec_free_context(CPCodecContext** ctx) { - if (!ctx || !*ctx) { - return; - } - cpcodec_close(*ctx); - delete *ctx; - *ctx = nullptr; -} - -// 输入侧 API:推送一帧串口数据,内部队列通常会缓冲。 -int cpcodec_send_packet(CPCodecContext* ctx, const CPPacket* packet) { - if (!ctx || !packet) { - return CP_ERROR_INVALID_ARGUMENT; - } - if (!ctx->is_open || !ctx->codec) { - return CP_ERROR_NOT_OPEN; - } - if (!ctx->codec->send_packet) { - return CP_ERROR_INVALID_STATE; - } - return ctx->codec->send_packet(ctx, *packet); -} - -// 输出侧 API:尝试从编解码器读取一帧解码结果。 -int cpcodec_receive_frame(CPCodecContext* ctx, CPFrame* frame) { - if (!ctx || !frame) { - return CP_ERROR_INVALID_ARGUMENT; - } - if (!ctx->is_open || !ctx->codec) { - return CP_ERROR_NOT_OPEN; - } - if (!ctx->codec->receive_frame) { - return CP_ERROR_INVALID_STATE; - } - return ctx->codec->receive_frame(ctx, *frame); -} - -} // namespace ffmsep diff --git a/components/comment/cpdecoder.hh b/components/comment/cpdecoder.hh deleted file mode 100644 index 411e35a..0000000 --- a/components/comment/cpdecoder.hh +++ /dev/null @@ -1,141 +0,0 @@ -// -// FFmpeg 风格的串口解码工具核心定义(带详细中文注释)。 -// 说明:本文件使用了 C++17 引入的 std::optional(可选值容器)与 [[nodiscard]] 属性, -// 以及 C++11 的 std::initializer_list、基于 enum class 的强类型枚举,用于提高类型安全。 -// - -#pragma once - -#include // 固定宽度整数类型,便于协议字段与位宽一致 -#include -#include // 互斥锁,用于注册表线程安全 -#include // C++17 可选类型,用来表示可能为空的查找结果 -#include -#include // C++17 字符串视图,避免不必要的拷贝 -#include -#include // C++11 可初始化列表,支持批量注册编解码器 - -namespace ffmsep { // 命名空间隔离所有与串口编解码相关的类型 - -// 错误码定义:参考 FFmpeg 的返回值约定,所有负数表示异常。 -inline constexpr int CP_SUCCESS = 0; // 成功 -inline constexpr int CP_ERROR_EOF = -1; // 流结束(End Of File) -inline constexpr int CP_ERROR_EAGAIN = -2; // 数据暂不可用,稍后重试 -inline constexpr int CP_ERROR_NOT_OPEN = -3; // 编解码上下文尚未打开 -inline constexpr int CP_ERROR_INVALID_STATE = -4; // 当前状态不允许该操作 -inline constexpr int CP_ERROR_INVALID_ARGUMENT = -5;// 传入参数不合法 - -// enum class 使用 C++11 强类型枚举,避免隐式转换导致的错误。 -enum class CPMediaType : std::uint8_t { - Unknown = 0, // 未知类型:默认值 - Data, // 数据流(例如串口数据) - Audio, // 音频流(预留扩展) - Video // 视频流(预留扩展) -}; - -// 编解码器 ID,用于在注册表中快速定位具体实现。 -enum class CPCodecID : std::uint32_t { - Unknown = 0, // 未知或未设置 - Tactile = 0x54514354u // 'T','Q','C','T':触觉传感器协议标识 -}; - -// CPPacket 表示输入的“帧包”,与 FFmpeg 中 AVPacket 的思想类似。 -struct CPPacket { - std::vector payload; // 有效载荷:串口原始数据 - std::int64_t pts = 0; // 显示时间戳(presentation timestamp) - std::int64_t dts = 0; // 解码时间戳(decode timestamp) - bool end_of_stream = false; // 标记此包是否为流结尾 - bool flush = false; // 是否请求刷新内部状态(例如重置缓冲) - - CPPacket() = default; - CPPacket(std::vector data, std::int64_t pts_value = 0, std::int64_t dts_value = 0) noexcept - : payload(std::move(data)), pts(pts_value), dts(dts_value) {} - - [[nodiscard]] bool empty() const noexcept { return payload.empty(); } // [[nodiscard]] 防止忽略返回值 -}; - -// CPFrame 表示解码后的数据帧,对应业务层可消费的实体。 -struct CPFrame { - std::vector data; // 解码后的数据内容 - std::int64_t pts = 0; // 与输入包对应的时间戳 - bool key_frame = false; // 是否为关键帧(例如起始帧) - bool valid = false; // 是否包含有效数据 - - void reset() noexcept { - data.clear(); - pts = 0; - key_frame = false; - valid = false; - } -}; - -struct CPCodecContext; - -// CPCodec 用函数指针描述具体编解码器的行为,等价于 FFmpeg 中的 AVCodec。 -struct CPCodec { - using InitFn = int (*)(CPCodecContext*); // 初始化回调 - using CloseFn = void (*)(CPCodecContext*); // 关闭回调 - using SendPacketFn = int (*)(CPCodecContext*, const CPPacket&); // 发送输入包 - using ReceiveFrameFn = int (*)(CPCodecContext*, CPFrame&); // 拉取输出帧 - - const char* name = nullptr; - const char* long_name = nullptr; - CPMediaType type = CPMediaType::Unknown; - CPCodecID id = CPCodecID::Unknown; - std::size_t priv_data_size = 0; - InitFn init = nullptr; - CloseFn close = nullptr; - SendPacketFn send_packet = nullptr; - ReceiveFrameFn receive_frame = nullptr; -}; - -struct CPCodecContext { - const CPCodec* codec = nullptr; // 指向当前使用的编解码器描述 - void* priv_data = nullptr; // 指向编解码器私有状态(大小由 priv_data_size 控制) - CPMediaType codec_type = CPMediaType::Unknown; // 保存媒体类型,便于外部查询 - bool is_open = false; // 是否已经成功调用 open - - void clear() noexcept { - codec = nullptr; - priv_data = nullptr; - codec_type = CPMediaType::Unknown; - is_open = false; - priv_storage.clear(); - } - - void* ensure_priv_storage(std::size_t size); // 确保私有存储空间足够,不足时重新分配 - void release_priv_storage() noexcept; // 释放私有存储 - - template - [[nodiscard]] T* priv_as() noexcept { - return static_cast(priv_data); - } - - template - [[nodiscard]] const T* priv_as() const noexcept { - return static_cast(priv_data); - } - -private: - std::vector priv_storage; // 私有缓冲区,使用 std::vector 管理生命周期 - - friend CPCodecContext* cpcodec_alloc_context3(const CPCodec*); - friend int cpcodec_open2(CPCodecContext*, const CPCodec*); - friend int cpcodec_close(CPCodecContext*); -}; - -// 注册接口:允许外部模块将编解码器加入全局列表。 -void cpcodec_register(const CPCodec* codec); -void cpcodec_register_many(std::initializer_list codecs); -const CPCodec* cpcodec_find_decoder(CPCodecID id); -const CPCodec* cpcodec_find_decoder_by_name(std::string_view name); -std::vector cpcodec_list_codecs(); - -CPCodecContext* cpcodec_alloc_context3(const CPCodec* codec); // 分配上下文,关联指定编解码器 -int cpcodec_open2(CPCodecContext* ctx, const CPCodec* codec = nullptr); // 打开上下文,可在此处指定或替换编解码器 -int cpcodec_close(CPCodecContext* ctx); // 关闭上下文并释放资源 -void cpcodec_free_context(CPCodecContext** ctx); // 释放上下文指针 -int cpcodec_send_packet(CPCodecContext* ctx, const CPPacket* packet); // 推送一份待解码的数据包 -int cpcodec_receive_frame(CPCodecContext* ctx, CPFrame* frame); // 拉取解码出来的帧 - -} // namespace ffmsep diff --git a/components/comment/tactile/tacdec.h b/components/comment/tactile/tacdec.h deleted file mode 100644 index b4e6bc1..0000000 --- a/components/comment/tactile/tacdec.h +++ /dev/null @@ -1,56 +0,0 @@ -// -// 触觉传感器串口协议高层解析工具(中文注释版)。 -// 注意:文件使用了 C++17 的 std::optional 与 inline constexpr, -// 可提供编译期常量与安全的可空返回值。 -// - -#pragma once - -#include "../cpdecoder.hh" - -#include // 协议字段均以固定宽度字节表示 -#include // std::optional:可能解析失败时返回空 -#include - -namespace ffmsep::tactile { - -inline constexpr std::uint8_t kStartByte = 0x3A; // 帧起始符(冒号) -inline constexpr std::uint8_t kEndByteFirst = 0x0D; // 帧结束符(回车) -inline constexpr std::uint8_t kEndByteSecond = 0x0A; // 帧结束符(换行) - -// 功能码枚举:使用 enum class 提升类型安全,避免与其他数值混用。 -enum class FunctionCode : std::uint8_t { - Unknown = 0x00, // 未知功能,解析失败或未初始化 - ReadMatrix = 0x01, // 读取整块矩阵 AD 值 - ReadSingle = 0x02, // 读取单点 AD 值 - ReadTemperature = 0x03,// 读取温度数据 - SetDeviceId = 0x51, // 修改设备编号 - SetMatrixSize = 0x52, // 修改矩阵尺寸(长边/短边) - CalibrationMode = 0x53 // 进入校准模式 -}; - -struct MatrixSize { - std::uint8_t long_edge = 0; // 矩阵长边尺寸 - std::uint8_t short_edge = 0; // 矩阵短边尺寸 -}; - -struct TactileFrame { - std::uint8_t device_address = 0; // 设备地址(1~255) - FunctionCode function = FunctionCode::Unknown; // 功能码 - std::uint8_t data_length = 0; // 数据域长度 - std::vector payload; // 数据域内容(按协议解析) -}; - -// 将底层 CPFrame 解析为结构化的 TactileFrame。 -std::optional parse_frame(const CPFrame& frame); -// 将数据域按小端 16 位压力值数组解析。 -std::vector parse_pressure_values(const TactileFrame& frame); -// 解析矩阵尺寸(用于读/写矩阵尺寸的命令)。 -std::optional parse_matrix_size_payload(const TactileFrame& frame); -// 解析矩阵坐标(复用尺寸结构,分别表示长边/短边索引)。 -std::optional parse_matrix_coordinate_payload(const TactileFrame& frame); - -const CPCodec* tactile_codec(); -void register_tactile_codec(); - -} // namespace ffmsep::tactile diff --git a/components/comment/tactile/tecdec.cc b/components/comment/tactile/tecdec.cc deleted file mode 100644 index 6a3ec67..0000000 --- a/components/comment/tactile/tecdec.cc +++ /dev/null @@ -1,297 +0,0 @@ -// -// 触觉传感器串口协议解码器实现(中文注释版)。 -// 使用要点: -// 1. 通过循环缓冲累积串口字节,按起始/结束符重组完整帧; -// 2. 使用 C++17 std::vector/std::optional 管理内存与返回值; -// 3. placement new( 头文件)在已有内存上构造上下文对象。 -// - -#include "tacdec.h" - -#include // std::find 等算法,用于搜索起始符 -#include -#include -#include // placement new:在已分配内存上构造对象 - -namespace ffmsep::tactile { // 与头文件保持一致的命名空间层次 - -namespace { // 匿名命名空间,用于封装文件内部的工具常量与函数 - -constexpr std::size_t kMinimumFrameSize = 1 // start 起始符 - + 1 // address 设备地址 - + 1 // function 功能码 - + 1 // length 数据长度 - + 0 // payload 最短为 0 - + 2 // CRC 校验 - + 2; // end markers 结束符 - -constexpr std::uint16_t kCrcInitial = 0xFFFF; // CRC 初始值 -constexpr std::uint16_t kCrcPolynomial = 0xA001; // CRC-16/MODBUS 多项式 (LSB-first) - -// 解码器私有上下文,跟随 CPCodecContext 的 priv_data 生命周期。 -struct TactileDecoderContext { - std::vector fifo; // 环形缓冲,存储尚未解析的原始字节 - bool end_of_stream = false; // 标记是否收到流结尾 - std::int64_t next_pts = 0; // 输出帧的自增 pts -}; - -std::uint16_t crc16_modbus(const std::uint8_t* data, std::size_t length) { - std::uint16_t crc = kCrcInitial; - for (std::size_t i = 0; i < length; ++i) { - crc ^= static_cast(data[i]); - for (int bit = 0; bit < 8; ++bit) { - if ((crc & 0x0001U) != 0U) { - crc = static_cast((crc >> 1U) ^ kCrcPolynomial); // LSB 为 1 时异或多项式 - } else { - crc = static_cast(crc >> 1U); // 否则右移 - } - } - } - return crc; -} - -TactileDecoderContext* get_priv(CPCodecContext* ctx) { - return ctx ? ctx->priv_as() : nullptr; // 使用模板封装的安全转换 -} - -int tactile_init(CPCodecContext* ctx) { - if (!ctx) { - return CP_ERROR_INVALID_ARGUMENT; - } - if (!ctx->priv_data) { - ctx->ensure_priv_storage(sizeof(TactileDecoderContext)); - } - auto* storage = static_cast(ctx->priv_data); - new (storage) TactileDecoderContext(); // placement new:在已分配的缓冲区中原地构造对象 - return CP_SUCCESS; -} - -void tactile_close(CPCodecContext* ctx) { - if (!ctx || !ctx->priv_data) { - return; - } - if (auto* priv = get_priv(ctx); priv != nullptr) { - priv->~TactileDecoderContext(); // 显式调用析构函数,释放内部 std::vector - } -} - -int tactile_send_packet(CPCodecContext* ctx, const CPPacket& packet) { - auto* priv = get_priv(ctx); - if (!priv) { - return CP_ERROR_INVALID_STATE; - } - - if (packet.flush) { - priv->fifo.clear(); // flush: 清除所有缓存,回到初始状态 - priv->end_of_stream = false; - priv->next_pts = 0; - } - - if (!packet.payload.empty()) { - priv->fifo.insert(priv->fifo.end(), packet.payload.begin(), packet.payload.end()); // 拼接新字节 - } - - if (packet.end_of_stream) { - priv->end_of_stream = true; // 标记输入源已结束 - } - - return CP_SUCCESS; -} - -std::size_t frame_length_from_payload(std::uint8_t payload_length) { - return 1U + 1U + 1U + 1U + payload_length + 2U + 2U; // 计算完整帧总长度 -} - -const std::uint8_t* buffer_data(const std::vector& buf) { - return buf.empty() ? nullptr : buf.data(); -} - -int tactile_receive_frame(CPCodecContext* ctx, CPFrame& frame) { - auto* priv = get_priv(ctx); - if (!priv) { - return CP_ERROR_INVALID_STATE; - } - - auto& buf = priv->fifo; - - while (true) { - if (buf.empty()) { - if (priv->end_of_stream) { - priv->end_of_stream = false; - return CP_ERROR_EOF; // 没有数据且流结束 - } - return CP_ERROR_EAGAIN; // 告诉调用者需要更多数据 - } - - // Discard bytes until start byte is found. - auto start_it = std::find(buf.begin(), buf.end(), kStartByte); - if (start_it == buf.end()) { - buf.clear(); - if (priv->end_of_stream) { - priv->end_of_stream = false; - return CP_ERROR_EOF; - } - return CP_ERROR_EAGAIN; - } - if (start_it != buf.begin()) { - buf.erase(buf.begin(), start_it); // 丢掉起始符前的噪声 - } - - if (buf.size() < kMinimumFrameSize) { - if (priv->end_of_stream) { - // Incomplete frame at end of stream: drop it and report EOF. - buf.clear(); - priv->end_of_stream = false; - return CP_ERROR_EOF; - } - return CP_ERROR_EAGAIN; // 帧尚未完整,继续等待 - } - - const std::uint8_t* data = buffer_data(buf); - const std::uint8_t address = data[1]; - const std::uint8_t function = data[2]; - const std::uint8_t payload_length = data[3]; - - const std::size_t total_frame_length = frame_length_from_payload(payload_length); // 根据长度字段推算完整帧尺寸 - if (buf.size() < total_frame_length) { - if (priv->end_of_stream) { - // Not enough data before stream end: treat as EOF and drop buffer. - buf.clear(); - priv->end_of_stream = false; - return CP_ERROR_EOF; - } - return CP_ERROR_EAGAIN; - } - - const std::size_t payload_offset = 4U; - const std::size_t crc_offset = payload_offset + payload_length; - const std::size_t end_offset = crc_offset + 2U; // CRC 后紧接 0x0D 0x0A - - const std::uint8_t crc_lo = data[crc_offset]; - const std::uint8_t crc_hi = data[crc_offset + 1U]; - const std::uint16_t crc_value = static_cast(crc_lo) | - static_cast(crc_hi << 8U); - - const std::uint8_t end_first = data[end_offset]; - const std::uint8_t end_second = data[end_offset + 1U]; - - if (end_first != kEndByteFirst || end_second != kEndByteSecond) { - // Invalid end marker, drop start byte and retry. - buf.erase(buf.begin()); - continue; - } - - const std::size_t crc_region_length = 3U + payload_length; // address + function + length + payload - const std::uint16_t computed_crc = crc16_modbus(data + 1U, crc_region_length); - if (computed_crc != crc_value) { - buf.erase(buf.begin()); // CRC 校验失败,丢弃该起始符并重新同步 - continue; - } - - (void)address; // 当前实现仅检查 CRC,不直接使用地址/功能码 - (void)function; - - frame.data.assign(buf.begin(), buf.begin() + static_cast(total_frame_length)); - frame.pts = priv->next_pts++; - frame.key_frame = true; - frame.valid = true; - - buf.erase(buf.begin(), buf.begin() + static_cast(total_frame_length)); // 移除已消费的内容 - - return CP_SUCCESS; - } -} - -const CPCodec kTactileCodec { - "tactile_serial", - "Framed tactile sensor serial protocol decoder", - CPMediaType::Data, - CPCodecID::Tactile, - sizeof(TactileDecoderContext), - &tactile_init, - &tactile_close, - &tactile_send_packet, - &tactile_receive_frame -}; - -} // namespace - -// 将底层 CPFrame 转换为协议专用的 TactileFrame,失败时返回 std::nullopt。 -std::optional parse_frame(const CPFrame& frame) { - if (!frame.valid || frame.data.size() < kMinimumFrameSize) { - return std::nullopt; - } - const auto* bytes = frame.data.data(); - const std::size_t size = frame.data.size(); - - if (bytes[0] != kStartByte) { - return std::nullopt; - } - if (bytes[size - 2] != kEndByteFirst || bytes[size - 1] != kEndByteSecond) { - return std::nullopt; - } - - if (size < 4U) { - return std::nullopt; - } - - const std::uint8_t length = bytes[3]; - if (frame_length_from_payload(length) != size) { - return std::nullopt; - } - - const std::uint8_t address = bytes[1]; - const FunctionCode function = static_cast(bytes[2]); - const std::size_t payload_offset = 4U; - - TactileFrame parsed{}; - parsed.device_address = address; - parsed.function = function; - parsed.data_length = length; - parsed.payload.assign(bytes + payload_offset, bytes + payload_offset + length); - return parsed; -} - -// 将数据域按照小端排列的 16 位压力值序列解析为整数数组。 -std::vector parse_pressure_values(const TactileFrame& frame) { - if (frame.payload.empty() || (frame.payload.size() % 2U != 0U)) { - return {}; - } - std::vector values; - values.reserve(frame.payload.size() / 2U); - for (std::size_t idx = 0; idx + 1U < frame.payload.size(); idx += 2U) { - const std::uint16_t value = static_cast( - static_cast(frame.payload[idx]) | - static_cast(frame.payload[idx + 1U] << 8U)); - values.push_back(value); - } - return values; -} - -// 解析矩阵尺寸载荷:两个字节分别为长边/短边。 -std::optional parse_matrix_size_payload(const TactileFrame& frame) { - if (frame.payload.size() != 2U) { - return std::nullopt; - } - MatrixSize size{}; - size.long_edge = frame.payload[0]; - size.short_edge = frame.payload[1]; - return size; -} - -// 解析矩阵坐标:协议格式与尺寸一致,直接复用逻辑。 -std::optional parse_matrix_coordinate_payload(const TactileFrame& frame) { - return parse_matrix_size_payload(frame); -} - -// 提供对外查询:返回触觉协议对应的 CPCodec 描述。 -const CPCodec* tactile_codec() { - return &kTactileCodec; -} - -// 将触觉协议编解码器注册到全局列表,供 cpcodec_find_decoder 使用。 -void register_tactile_codec() { - cpcodec_register(&kTactileCodec); -} - -} // namespace ffmsep::tactile diff --git a/components/ffmsep/cpdecoder.cc b/components/ffmsep/cpdecoder.cc index e59381d..cf2b97d 100644 --- a/components/ffmsep/cpdecoder.cc +++ b/components/ffmsep/cpdecoder.cc @@ -1,14 +1,14 @@ -// -// Core FFmpeg-style codec registry and decoding helpers. -// - #include "cpdecoder.hh" +#include "components/ffmsep/cpdecoder.hh" #include +#include +#include #include +#include +#include namespace ffmsep { - namespace { std::vector& codec_registry() { @@ -28,7 +28,7 @@ void attach_codec(CPCodecContext* ctx, const CPCodec* codec) { ctx->codec = codec; if (!codec) { - ctx->codec_type = CPMediaType::Unknown; + ctx->codec_type = CPMediaType::Data; ctx->priv_data = nullptr; ctx->release_priv_storage(); return; @@ -42,11 +42,12 @@ bool codec_name_equals(const CPCodec* codec, std::string_view name) { if (!codec || !codec->name) { return false; } + return std::string_view(codec->name) == name; } - } // namespace +using namespace ffmsep; void* CPCodecContext::ensure_priv_storage(std::size_t size) { if (size == 0U) { priv_storage.clear(); @@ -65,7 +66,7 @@ void CPCodecContext::release_priv_storage() noexcept { priv_data = nullptr; } -void cpcodec_register(const CPCodec* codec) { +void cpcodec_register(const CPCodec *codec) { if (!codec || !codec->name) { return; } @@ -76,9 +77,9 @@ void cpcodec_register(const CPCodec* codec) { if (already != reg.end()) { return; } - + auto same_id = std::find_if(reg.begin(), reg.end(), [codec](const CPCodec* entry) { - return entry && codec && entry->id == codec->id && codec->id != CPCodecID::Unknown; + return entry && codec && entry->id == codec->id && codec->id != CPCodecID::Unknow; }); if (same_id != reg.end()) { *same_id = codec; @@ -97,7 +98,7 @@ void cpcodec_register_many(std::initializer_list codecs) { const CPCodec* cpcodec_find_decoder(CPCodecID id) { std::lock_guard lock(registry_mutex()); const auto& reg = codec_registry(); - auto it = std::find_if(reg.begin(), reg.end(), [id](const CPCodec* codec) { + auto it = std::find_if(reg.begin(), reg.end(), [id](const CPCodec* codec){ return codec && codec->id == id; }); return it == reg.end() ? nullptr : *it; @@ -106,10 +107,11 @@ const CPCodec* cpcodec_find_decoder(CPCodecID id) { const CPCodec* cpcodec_find_decoder_by_name(std::string_view name) { std::lock_guard lock(registry_mutex()); const auto& reg = codec_registry(); - auto it = std::find_if(reg.begin(), reg.end(), [name](const CPCodec* codec) { + auto it = std::find_if(reg.begin(), reg.end(), [name](const CPCodec* codec){ return codec_name_equals(codec, name); }); - return it == reg.end() ? nullptr : *it; + + return it ==reg.end() ? nullptr : *it; } std::vector cpcodec_list_codecs() { @@ -117,15 +119,16 @@ std::vector cpcodec_list_codecs() { return codec_registry(); } -CPCodecContext* cpcodec_alloc_context3(const CPCodec* codec) { +CPCodecContext* cpcodec_alloc_context(const CPCodec* codec) { auto* ctx = new CPCodecContext(); if (codec) { attach_codec(ctx, codec); } + return ctx; } -int cpcodec_open2(CPCodecContext* ctx, const CPCodec* codec) { +int cpcodec_open(CPCodecContext* ctx, const CPCodec* codec) { if (!ctx) { return CP_ERROR_INVALID_ARGUMENT; } @@ -153,10 +156,11 @@ int cpcodec_open2(CPCodecContext* ctx, const CPCodec* codec) { return rc; } } + return CP_SUCCESS; } -int cpcodec_close(CPCodecContext* ctx) { +int cpcodec_close(CPCodecContext *ctx) { if (!ctx) { return CP_ERROR_INVALID_ARGUMENT; } @@ -169,28 +173,33 @@ int cpcodec_close(CPCodecContext* ctx) { ctx->codec->close(ctx); } + if (ctx->codec && ctx->codec->close) { + ctx->codec->close(ctx); + } + ctx->is_open = false; ctx->release_priv_storage(); - ctx->codec_type = CPMediaType::Unknown; + ctx->codec_type = CPMediaType::Unknow; ctx->codec = nullptr; ctx->priv_data = nullptr; return CP_SUCCESS; } -void cpcodec_free_context(CPCodecContext** ctx) { +void cpcodec_free_context(CPCodecContext **ctx) { if (!ctx || !*ctx) { return; } + cpcodec_close(*ctx); delete *ctx; *ctx = nullptr; } -int cpcodec_send_packet(CPCodecContext* ctx, const CPPacket* packet) { +int cpcodec_send_packet(CPCodecContext *ctx, const CPPacket *packet) { if (!ctx || !packet) { return CP_ERROR_INVALID_ARGUMENT; } - if (!ctx->is_open || !ctx->codec) { + if (!ctx || !ctx->codec) { return CP_ERROR_NOT_OPEN; } if (!ctx->codec->send_packet) { @@ -199,7 +208,7 @@ int cpcodec_send_packet(CPCodecContext* ctx, const CPPacket* packet) { return ctx->codec->send_packet(ctx, *packet); } -int cpcodec_receive_frame(CPCodecContext* ctx, CPFrame* frame) { +int cpcodec_receive_frame(CPCodecContext *ctx, CPFrame *frame) { if (!ctx || !frame) { return CP_ERROR_INVALID_ARGUMENT; } @@ -211,5 +220,4 @@ int cpcodec_receive_frame(CPCodecContext* ctx, CPFrame* frame) { } return ctx->codec->receive_frame(ctx, *frame); } - -} // namespace ffmsep +} \ No newline at end of file diff --git a/components/ffmsep/cpdecoder.hh b/components/ffmsep/cpdecoder.hh index 0a570c0..6966100 100644 --- a/components/ffmsep/cpdecoder.hh +++ b/components/ffmsep/cpdecoder.hh @@ -1,9 +1,6 @@ -// -// Simple FFmpeg-inspired serial decoding toolkit. -// - #pragma once +#include "components/ffmsep/cpdecoder.hh" #include #include #include @@ -15,7 +12,6 @@ namespace ffmsep { -// Error codes loosely mirroring FFmpeg semantics. inline constexpr int CP_SUCCESS = 0; inline constexpr int CP_ERROR_EOF = -1; inline constexpr int CP_ERROR_EAGAIN = -2; @@ -24,15 +20,13 @@ inline constexpr int CP_ERROR_INVALID_STATE = -4; inline constexpr int CP_ERROR_INVALID_ARGUMENT = -5; enum class CPMediaType : std::uint8_t { - Unknown = 0, + Unknow = 0, Data, - Audio, - Video }; enum class CPCodecID : std::uint32_t { - Unknown = 0, - Tactile = 0x54514354u // 'T','Q','C','T' marker for tactile quick codec. + Unknow = 0, + Tactile = 0x54514354u // 'T','Q','C','T':触觉传感器协议标识 Tactile Quick Codec Type }; struct CPPacket { @@ -46,7 +40,7 @@ struct CPPacket { CPPacket(std::vector data, std::int64_t pts_value = 0, std::int64_t dts_value = 0) noexcept : payload(std::move(data)), pts(pts_value), dts(dts_value) {} - [[nodiscard]] bool empty() const noexcept { return payload.empty(); } + [[nodiscard]] bool empty() const noexcept {return payload.empty();} }; struct CPFrame { @@ -57,24 +51,24 @@ struct CPFrame { void reset() noexcept { data.clear(); - pts = 0; key_frame = false; valid = false; + pts = 0; } }; struct CPCodecContext; struct CPCodec { - using InitFn = int (*)(CPCodecContext*); - using CloseFn = void (*)(CPCodecContext*); - using SendPacketFn = int (*)(CPCodecContext*, const CPPacket&); - using ReceiveFrameFn = int (*)(CPCodecContext*, CPFrame&); + using InitFn = int(*)(CPCodecContext*); + using CloseFn = void(*)(CPCodecContext*); + using SendPacketFn = int(*)(CPCodecContext*, const CPPacket&); + using ReceiveFrameFn = int(*)(CPCodecContext*, CPFrame&); const char* name = nullptr; const char* long_name = nullptr; - CPMediaType type = CPMediaType::Unknown; - CPCodecID id = CPCodecID::Unknown; + CPMediaType type = CPMediaType::Unknow; + CPCodecID id = CPCodecID::Unknow; std::size_t priv_data_size = 0; InitFn init = nullptr; CloseFn close = nullptr; @@ -85,13 +79,13 @@ struct CPCodec { struct CPCodecContext { const CPCodec* codec = nullptr; void* priv_data = nullptr; - CPMediaType codec_type = CPMediaType::Unknown; + CPMediaType codec_type = CPMediaType::Unknow; bool is_open = false; void clear() noexcept { codec = nullptr; priv_data = nullptr; - codec_type = CPMediaType::Unknown; + codec_type = CPMediaType::Unknow; is_open = false; priv_storage.clear(); } @@ -99,21 +93,20 @@ struct CPCodecContext { void* ensure_priv_storage(std::size_t size); void release_priv_storage() noexcept; - template + template [[nodiscard]] T* priv_as() noexcept { return static_cast(priv_data); } - template + template [[nodiscard]] const T* priv_as() const noexcept { return static_cast(priv_data); } private: std::vector priv_storage; - - friend CPCodecContext* cpcodec_alloc_context3(const CPCodec*); - friend int cpcodec_open2(CPCodecContext*, const CPCodec*); + friend CPCodecContext* cpcodec_alloc_context(const CPCodec*); + friend int cpcodec_open(CPCodecContext*, const CPCodec*); friend int cpcodec_close(CPCodecContext*); }; @@ -123,11 +116,10 @@ const CPCodec* cpcodec_find_decoder(CPCodecID id); const CPCodec* cpcodec_find_decoder_by_name(std::string_view name); std::vector cpcodec_list_codecs(); -CPCodecContext* cpcodec_alloc_context3(const CPCodec* codec); -int cpcodec_open2(CPCodecContext* ctx, const CPCodec* codec = nullptr); -int cpcodec_close(CPCodecContext* ctx); -void cpcodec_free_context(CPCodecContext** ctx); -int cpcodec_send_packet(CPCodecContext* ctx, const CPPacket* packet); -int cpcodec_receive_frame(CPCodecContext* ctx, CPFrame* frame); - -} // namespace ffmsep +CPCodecContext* cpcodec_alloc_context(const CPCodec* codec); +int cpcodec_open(CPCodecContext*, const CPCodec*); +int cpcodec_close(CPCodecContext*); +void cpcodec_free_context(CPCodecContext **ctx); +int cpcodec_send_packet(CPCodecContext*, const CPPacket*); +int cpcodec_receive_frame(CPCodecContext*, CPFrame*); +} \ No newline at end of file diff --git a/components/ffmsep/cpstream_core.cc b/components/ffmsep/cpstream_core.cc new file mode 100644 index 0000000..a70a355 --- /dev/null +++ b/components/ffmsep/cpstream_core.cc @@ -0,0 +1,627 @@ +#include "components/ffmsep/cpstream_core.hh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ffmsep { + +namespace { + +constexpr auto kReaderIdleSleep = std::chrono::milliseconds(5); +constexpr auto kDecoderIdleSleep = std::chrono::milliseconds(1); + +const CPCodec* resolve_requested_codec(const CPStreamConfig& config) { + if (!config.codec_name.empty()) { + if (const CPCodec* codec = cpcodec_find_decoder_by_name(config.codec_name)) { + return codec; + } + } + if (config.codec_id != CPCodecID::Unknow) { + if (const CPCodec* codec = cpcodec_find_decoder(config.codec_id)) { + return codec; + } + } + return nullptr; +} + +} // namespace + +struct CPStreamCore::Impl { + struct Packet { + std::vector payload; + std::int64_t pts = 0; + bool end_of_stream = false; + bool flush = false; + }; + + explicit Impl(CPStreamConfig config) + : config_(std::move(config)) { + normalize_config(); + } + + ~Impl() = default; + + void normalize_config() { + if (config_.read_chunk_size == 0U) { + config_.read_chunk_size = 256U; + } + if (config_.packet_queue_capacity == 0U) { + config_.packet_queue_capacity = 1U; + } + if (config_.frame_queue_capacity == 0U) { + config_.frame_queue_capacity = 1U; + } + frame_queue_capacity_ = config_.frame_queue_capacity; + } + + bool open(const CPStreamConfig& cfg) { + stop(); + close(); + + config_ = cfg; + normalize_config(); + + if (config_.port.empty()) { + set_last_error("serial port is empty"); + return false; + } + + codec_descriptor_ = resolve_requested_codec(config_); + if (!codec_descriptor_) { + set_last_error("codec not found for requested identifier"); + return false; + } + + codec_ctx_ = cpcodec_alloc_context(codec_descriptor_); + if (!codec_ctx_) { + set_last_error("failed to allocate codec context"); + return false; + } + + int rc = cpcodec_open(codec_ctx_, codec_descriptor_); + if (rc < CP_SUCCESS) { + set_last_error("failed to open codec context: error " + std::to_string(rc)); + cpcodec_free_context(&codec_ctx_); + codec_ctx_ = nullptr; + return false; + } + + try { + auto serial = std::make_shared( + config_.port, + config_.baudrate, + config_.timeout, + config_.bytesize, + config_.parity, + config_.stopbits, + config_.flowcontrol); + serial->open(); + serial->flush(); + + { + std::lock_guard lock(serial_mutex_); + serial_ = std::move(serial); + } + } catch (const serial::IOException& ex) { + set_last_error(ex.what() ? ex.what() : "serial IO exception"); + cpcodec_close(codec_ctx_); + cpcodec_free_context(&codec_ctx_); + codec_ctx_ = nullptr; + return false; + } catch (const serial::SerialException& ex) { + set_last_error(ex.what() ? ex.what() : "serial exception"); + cpcodec_close(codec_ctx_); + cpcodec_free_context(&codec_ctx_); + codec_ctx_ = nullptr; + return false; + } catch (const std::exception& ex) { + set_last_error(ex.what()); + cpcodec_close(codec_ctx_); + cpcodec_free_context(&codec_ctx_); + codec_ctx_ = nullptr; + return false; + } + + { + std::lock_guard lock(packet_mutex_); + packet_queue_.clear(); + } + { + std::lock_guard lock(frame_mutex_); + frame_queue_.clear(); + } + pts_counter_.store(0, std::memory_order_relaxed); + stop_requested_.store(false, std::memory_order_release); + set_last_error({}); + return true; + } + + bool open() { + return open(config_); + } + + bool reopen(const CPStreamConfig& cfg) { + return open(cfg); + } + + void close() { + stop(); + + { + std::lock_guard lock(serial_mutex_); + if (serial_) { + try { + if (serial_->isOpen()) { + serial_->close(); + } + } catch (...) { + // Ignore close errors. + } + serial_.reset(); + } + } + + if (codec_ctx_) { + cpcodec_close(codec_ctx_); + cpcodec_free_context(&codec_ctx_); + codec_ctx_ = nullptr; + } + + { + std::lock_guard lock(packet_mutex_); + packet_queue_.clear(); + } + { + std::lock_guard lock(frame_mutex_); + frame_queue_.clear(); + } + } + + bool start() { + if (running_.load(std::memory_order_acquire)) { + return true; + } + + std::shared_ptr serial_copy; + { + std::lock_guard lock(serial_mutex_); + serial_copy = serial_; + } + if (!serial_copy || !serial_copy->isOpen()) { + set_last_error("serial port is not open"); + return false; + } + + if (!codec_ctx_ || !codec_ctx_->is_open) { + set_last_error("codec context is not ready"); + return false; + } + + stop_requested_.store(false, std::memory_order_release); + running_.store(true, std::memory_order_release); + + reader_thread_ = std::thread(&Impl::reader_loop, this); + decoder_thread_ = std::thread(&Impl::decoder_loop, this); + return true; + } + + void stop() { + if (!running_.exchange(false, std::memory_order_acq_rel)) { + return; + } + + stop_requested_.store(true, std::memory_order_release); + packet_cv_.notify_all(); + + if (reader_thread_.joinable()) { + reader_thread_.join(); + } + + signal_decoder_flush(true); + packet_cv_.notify_all(); + + if (decoder_thread_.joinable()) { + decoder_thread_.join(); + } + + stop_requested_.store(false, std::memory_order_release); + + { + std::lock_guard lock(packet_mutex_); + packet_queue_.clear(); + } + + if (codec_ctx_ && codec_ctx_->is_open) { + reset_decoder(); + } + } + + bool is_open() const { + std::lock_guard lock(serial_mutex_); + return serial_ && serial_->isOpen(); + } + + bool is_running() const { + return running_.load(std::memory_order_acquire); + } + + bool send(const std::vector& data) { + return send(data.data(), data.size()); + } + + bool send(const std::uint8_t* data, std::size_t size) { + if (!data || size == 0U) { + return false; + } + + std::shared_ptr serial_copy; + { + std::lock_guard lock(serial_mutex_); + serial_copy = serial_; + } + + if (!serial_copy || !serial_copy->isOpen()) { + set_last_error("serial port is not open"); + return false; + } + + try { + const auto written = serial_copy->write(data, size); + return written == size; + } catch (const serial::IOException& ex) { + set_last_error(ex.what() ? ex.what() : "serial IO exception"); + } catch (const serial::SerialException& ex) { + set_last_error(ex.what() ? ex.what() : "serial exception"); + } catch (const std::exception& ex) { + set_last_error(ex.what()); + } + return false; + } + + std::optional try_pop_frame() { + std::lock_guard lock(frame_mutex_); + if (frame_queue_.empty()) { + return std::nullopt; + } + DecodedFrame frame = std::move(frame_queue_.front()); + frame_queue_.pop_front(); + return frame; + } + + bool wait_for_frame(DecodedFrame& frame, std::chrono::milliseconds timeout) { + std::unique_lock lock(frame_mutex_); + if (!frame_cv_.wait_for(lock, timeout, [&] { + return !frame_queue_.empty(); + })) { + return false; + } + frame = std::move(frame_queue_.front()); + frame_queue_.pop_front(); + return true; + } + + void clear_frames() { + std::lock_guard lock(frame_mutex_); + frame_queue_.clear(); + } + + void set_frame_queue_capacity(std::size_t capacity) { + if (capacity == 0U) { + capacity = 1U; + } + { + std::lock_guard lock(frame_mutex_); + frame_queue_capacity_ = capacity; + config_.frame_queue_capacity = capacity; + while (frame_queue_.size() > frame_queue_capacity_) { + frame_queue_.pop_front(); + } + } + } + + void set_frame_callback(FrameCallback callback) { + std::lock_guard lock(callback_mutex_); + frame_callback_ = std::move(callback); + } + + CPStreamConfig config() const { + return config_; + } + + std::string last_error() const { + std::lock_guard lock(last_error_mutex_); + return last_error_; + } + + static std::vector list_ports() { + return serial::list_ports(); + } + + void reader_loop() { + std::vector buffer(config_.read_chunk_size); + + while (!stop_requested_.load(std::memory_order_acquire)) { + std::shared_ptr serial_copy; + { + std::lock_guard lock(serial_mutex_); + serial_copy = serial_; + } + if (!serial_copy || !serial_copy->isOpen()) { + std::this_thread::sleep_for(kReaderIdleSleep); + continue; + } + + std::size_t bytes_read = 0; + try { + bytes_read = serial_copy->read(buffer.data(), buffer.size()); + } catch (const serial::IOException& ex) { + set_last_error(ex.what() ? ex.what() : "serial IO exception"); + std::this_thread::sleep_for(kReaderIdleSleep); + continue; + } catch (const serial::SerialException& ex) { + set_last_error(ex.what() ? ex.what() : "serial exception"); + std::this_thread::sleep_for(kReaderIdleSleep); + continue; + } catch (const std::exception& ex) { + set_last_error(ex.what()); + std::this_thread::sleep_for(kReaderIdleSleep); + continue; + } + + if (bytes_read == 0U) { + std::this_thread::sleep_for(kReaderIdleSleep); + continue; + } + + Packet packet; + packet.payload.assign(buffer.begin(), buffer.begin() + static_cast(bytes_read)); + packet.pts = pts_counter_.fetch_add(1, std::memory_order_relaxed); + + { + std::lock_guard lock(packet_mutex_); + if (packet_queue_.size() >= config_.packet_queue_capacity) { + packet_queue_.pop_front(); + } + packet_queue_.push_back(std::move(packet)); + } + packet_cv_.notify_one(); + } + } + + void decoder_loop() { + while (true) { + Packet packet; + { + std::unique_lock lock(packet_mutex_); + packet_cv_.wait(lock, [&] { + return stop_requested_.load(std::memory_order_acquire) || !packet_queue_.empty(); + }); + + if (packet_queue_.empty()) { + if (stop_requested_.load(std::memory_order_acquire)) { + break; + } + continue; + } + + packet = std::move(packet_queue_.front()); + packet_queue_.pop_front(); + } + + if (!codec_ctx_ || !codec_ctx_->is_open) { + if (packet.end_of_stream) { + break; + } + std::this_thread::sleep_for(kDecoderIdleSleep); + continue; + } + + CPPacket cp_packet; + cp_packet.payload = std::move(packet.payload); + cp_packet.pts = packet.pts; + cp_packet.dts = packet.pts; + cp_packet.end_of_stream = packet.end_of_stream; + cp_packet.flush = packet.flush; + + int rc = cpcodec_send_packet(codec_ctx_, &cp_packet); + if (rc < CP_SUCCESS) { + if (packet.end_of_stream) { + break; + } + continue; + } + + while (true) { + CPFrame frame; + rc = cpcodec_receive_frame(codec_ctx_, &frame); + if (rc == CP_SUCCESS) { + DecodedFrame decoded; + decoded.pts = frame.pts; + decoded.received_at = std::chrono::steady_clock::now(); + decoded.frame = std::move(frame); + + FrameCallback callback_copy; + { + std::lock_guard lock(callback_mutex_); + callback_copy = frame_callback_; + } + if (callback_copy) { + callback_copy(decoded); + } + + { + std::lock_guard lock(frame_mutex_); + if (frame_queue_.size() >= frame_queue_capacity_) { + frame_queue_.pop_front(); + } + frame_queue_.push_back(std::move(decoded)); + } + frame_cv_.notify_one(); + } else if (rc == CP_ERROR_EAGAIN) { + break; + } else { + if (rc == CP_ERROR_EOF && packet.end_of_stream) { + return; + } + break; + } + } + + if (packet.end_of_stream) { + break; + } + } + } + + void signal_decoder_flush(bool end_of_stream) { + Packet packet; + packet.flush = true; + packet.end_of_stream = end_of_stream; + { + std::lock_guard lock(packet_mutex_); + packet_queue_.push_back(std::move(packet)); + } + packet_cv_.notify_one(); + } + + void reset_decoder() { + if (!codec_ctx_ || !codec_descriptor_) { + return; + } + cpcodec_close(codec_ctx_); + int rc = cpcodec_open(codec_ctx_, codec_descriptor_); + if (rc < CP_SUCCESS) { + set_last_error("failed to reset codec context: error " + std::to_string(rc)); + } + } + + void set_last_error(std::string message) { + std::lock_guard lock(last_error_mutex_); + last_error_ = std::move(message); + } + + CPStreamConfig config_{}; + const CPCodec* codec_descriptor_ = nullptr; + + std::shared_ptr serial_; + mutable std::mutex serial_mutex_; + + CPCodecContext* codec_ctx_ = nullptr; + + std::thread reader_thread_; + std::thread decoder_thread_; + + std::mutex packet_mutex_; + std::condition_variable packet_cv_; + std::deque packet_queue_; + + std::mutex frame_mutex_; + std::condition_variable frame_cv_; + std::deque frame_queue_; + std::size_t frame_queue_capacity_ = 16; + + FrameCallback frame_callback_; + mutable std::mutex callback_mutex_; + + std::atomic running_{false}; + std::atomic stop_requested_{false}; + std::atomic pts_counter_{0}; + + std::string last_error_; + mutable std::mutex last_error_mutex_; +}; + +CPStreamCore::CPStreamCore(CPStreamConfig config) + : impl_(std::make_unique(std::move(config))) {} + +CPStreamCore::~CPStreamCore() { + if (impl_) { + impl_->stop(); + impl_->close(); + } +} + +bool CPStreamCore::open(const CPStreamConfig& config) { + return impl_->open(config); +} + +bool CPStreamCore::open() { + return impl_->open(); +} + +bool CPStreamCore::reopen(const CPStreamConfig& config) { + return impl_->reopen(config); +} + +void CPStreamCore::close() { + impl_->close(); +} + +bool CPStreamCore::start() { + return impl_->start(); +} + +void CPStreamCore::stop() { + impl_->stop(); +} + +bool CPStreamCore::is_open() const noexcept { + return impl_->is_open(); +} + +bool CPStreamCore::is_running() const noexcept { + return impl_->is_running(); +} + +bool CPStreamCore::send(const std::vector& data) { + return impl_->send(data); +} + +bool CPStreamCore::send(const std::uint8_t* data, std::size_t size) { + return impl_->send(data, size); +} + +std::optional CPStreamCore::try_pop_frame() { + return impl_->try_pop_frame(); +} + +bool CPStreamCore::wait_for_frame(DecodedFrame& frame, std::chrono::milliseconds timeout) { + return impl_->wait_for_frame(frame, timeout); +} + +void CPStreamCore::clear_frames() { + impl_->clear_frames(); +} + +void CPStreamCore::set_frame_queue_capacity(std::size_t capacity) { + impl_->set_frame_queue_capacity(capacity); +} + +void CPStreamCore::set_frame_callback(FrameCallback callback) { + impl_->set_frame_callback(std::move(callback)); +} + +CPStreamConfig CPStreamCore::config() const { + return impl_->config(); +} + +std::string CPStreamCore::last_error() const { + return impl_->last_error(); +} + +std::vector CPStreamCore::list_available_ports() { + return Impl::list_ports(); +} + +} // namespace ffmsep diff --git a/components/ffmsep/cpstream_core.hh b/components/ffmsep/cpstream_core.hh new file mode 100644 index 0000000..d580d0d --- /dev/null +++ b/components/ffmsep/cpstream_core.hh @@ -0,0 +1,77 @@ +#pragma once + +#include "components/ffmsep/cpdecoder.hh" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ffmsep { + +struct DecodedFrame { + CPFrame frame; + std::chrono::steady_clock::time_point received_at{}; + std::int64_t pts = 0; +}; + +struct CPStreamConfig { + std::string port; + std::uint32_t baudrate = 115200; + serial::Timeout timeout = serial::Timeout::simpleTimeout(50); + serial::bytesize_t bytesize = serial::eightbits; + serial::parity_t parity = serial::parity_none; + serial::stopbits_t stopbits = serial::stopbits_one; + serial::flowcontrol_t flowcontrol = serial::flowcontrol_none; + std::size_t read_chunk_size = 256; + std::size_t packet_queue_capacity = 128; + std::size_t frame_queue_capacity = 16; + CPCodecID codec_id = CPCodecID::Unknow; + std::string codec_name; +}; + +class CPStreamCore { +public: + using FrameCallback = std::function; + + explicit CPStreamCore(CPStreamConfig config = {}); + ~CPStreamCore(); + + CPStreamCore(const CPStreamCore&) = delete; + CPStreamCore& operator=(const CPStreamCore&) = delete; + + bool open(const CPStreamConfig& config); + bool open(); + bool reopen(const CPStreamConfig& config); + void close(); + + bool start(); + void stop(); + + [[nodiscard]] bool is_open() const noexcept; + [[nodiscard]] bool is_running() const noexcept; + + bool send(const std::vector& data); + bool send(const std::uint8_t* data, std::size_t size); + + std::optional try_pop_frame(); + bool wait_for_frame(DecodedFrame& frame, std::chrono::milliseconds timeout); + void clear_frames(); + void set_frame_queue_capacity(std::size_t capacity); + + void set_frame_callback(FrameCallback callback); + + [[nodiscard]] CPStreamConfig config() const; + [[nodiscard]] std::string last_error() const; + + static std::vector list_available_ports(); + +private: + struct Impl; + std::unique_ptr impl_; +}; + +} // namespace ffmsep diff --git a/components/ffmsep/tactile/tecdec.cc b/components/ffmsep/tactile/tacdec.cc similarity index 82% rename from components/ffmsep/tactile/tecdec.cc rename to components/ffmsep/tactile/tacdec.cc index fa97e67..ce7a51f 100644 --- a/components/ffmsep/tactile/tecdec.cc +++ b/components/ffmsep/tactile/tacdec.cc @@ -1,28 +1,25 @@ -// -// Decoder for the tactile sensor framed binary protocol. -// - -#include "tacdec.h" - +#include "tacdec.hh" +#include "components/ffmsep/cpdecoder.hh" #include #include #include #include +#include +#include namespace ffmsep::tactile { - namespace { -constexpr std::size_t kMinimumFrameSize = 1 // start - + 1 // address - + 1 // function - + 1 // length - + 0 // payload - + 2 // CRC - + 2; // end markers +constexpr std::size_t kMinimumFrameSize = 1 + + 1 + + 1 + + 1 + + 0 + + 2 + + 2; constexpr std::uint16_t kCrcInitial = 0xFFFF; -constexpr std::uint16_t kCrcPolynomial = 0xA001; // CRC-16/MODBUS (LSB first) +constexpr std::uint16_t kCrcPolynomial = 0xA001; struct TactileDecoderContext { std::vector fifo; @@ -30,6 +27,14 @@ struct TactileDecoderContext { std::int64_t next_pts = 0; }; +std::size_t frame_length_from_payload(std::uint8_t payload_length) { + return 1U + 1U + 1U + 1U + payload_length + 2U + 2U; +} + +const std::uint8_t* buffer_data(const std::vector& buf) { + return buf.empty() ? nullptr : buf.data(); +} + std::uint16_t crc16_modbus(const std::uint8_t* data, std::size_t length) { std::uint16_t crc = kCrcInitial; for (std::size_t i = 0; i < length; ++i) { @@ -37,7 +42,8 @@ std::uint16_t crc16_modbus(const std::uint8_t* data, std::size_t length) { for (int bit = 0; bit < 8; ++bit) { if ((crc & 0x0001U) != 0U) { crc = static_cast((crc >> 1U) ^ kCrcPolynomial); - } else { + } + else { crc = static_cast(crc >> 1U); } } @@ -71,11 +77,10 @@ void tactile_close(CPCodecContext* ctx) { } int tactile_send_packet(CPCodecContext* ctx, const CPPacket& packet) { - auto* priv = get_priv(ctx); + auto priv = get_priv(ctx); if (!priv) { return CP_ERROR_INVALID_STATE; } - if (packet.flush) { priv->fifo.clear(); priv->end_of_stream = false; @@ -93,14 +98,6 @@ int tactile_send_packet(CPCodecContext* ctx, const CPPacket& packet) { return CP_SUCCESS; } -std::size_t frame_length_from_payload(std::uint8_t payload_length) { - return 1U + 1U + 1U + 1U + payload_length + 2U + 2U; -} - -const std::uint8_t* buffer_data(const std::vector& buf) { - return buf.empty() ? nullptr : buf.data(); -} - int tactile_receive_frame(CPCodecContext* ctx, CPFrame& frame) { auto* priv = get_priv(ctx); if (!priv) { @@ -118,7 +115,6 @@ int tactile_receive_frame(CPCodecContext* ctx, CPFrame& frame) { return CP_ERROR_EAGAIN; } - // Discard bytes until start byte is found. auto start_it = std::find(buf.begin(), buf.end(), kStartByte); if (start_it == buf.end()) { buf.clear(); @@ -128,13 +124,13 @@ int tactile_receive_frame(CPCodecContext* ctx, CPFrame& frame) { } return CP_ERROR_EAGAIN; } + if (start_it != buf.begin()) { buf.erase(buf.begin(), start_it); } if (buf.size() < kMinimumFrameSize) { if (priv->end_of_stream) { - // Incomplete frame at end of stream: drop it and report EOF. buf.clear(); priv->end_of_stream = false; return CP_ERROR_EOF; @@ -143,14 +139,13 @@ int tactile_receive_frame(CPCodecContext* ctx, CPFrame& frame) { } const std::uint8_t* data = buffer_data(buf); - const std::uint8_t address = data[1]; - const std::uint8_t function = data[2]; - const std::uint8_t payload_length = data[3]; - + const std::uint8_t address = data[1U]; + const FunctionCode function = static_cast(data[2U]); + const std::uint8_t payload_length = data[3U]; const std::size_t total_frame_length = frame_length_from_payload(payload_length); + if (buf.size() < total_frame_length) { if (priv->end_of_stream) { - // Not enough data before stream end: treat as EOF and drop buffer. buf.clear(); priv->end_of_stream = false; return CP_ERROR_EOF; @@ -171,12 +166,11 @@ int tactile_receive_frame(CPCodecContext* ctx, CPFrame& frame) { const std::uint8_t end_second = data[end_offset + 1U]; if (end_first != kEndByteFirst || end_second != kEndByteSecond) { - // Invalid end marker, drop start byte and retry. buf.erase(buf.begin()); continue; } - const std::size_t crc_region_length = 3U + payload_length; // address + function + length + payload + const std::size_t crc_region_length = 3U + payload_length; const std::uint16_t computed_crc = crc16_modbus(data + 1U, crc_region_length); if (computed_crc != crc_value) { buf.erase(buf.begin()); @@ -192,24 +186,22 @@ int tactile_receive_frame(CPCodecContext* ctx, CPFrame& frame) { frame.valid = true; buf.erase(buf.begin(), buf.begin() + static_cast(total_frame_length)); - return CP_SUCCESS; } } const CPCodec kTactileCodec { - "tactile_serial", - "Framed tactile sensor serial protocol decoder", - CPMediaType::Data, - CPCodecID::Tactile, - sizeof(TactileDecoderContext), - &tactile_init, - &tactile_close, - &tactile_send_packet, - &tactile_receive_frame + .name = "tactile_serial", + .long_name = "Framed tactile sensor serial protocol decoder", + .type = CPMediaType::Data, + .id = CPCodecID::Tactile, + .priv_data_size = sizeof(TactileDecoderContext), + .init = &tactile_init, + .close = &tactile_close, + .send_packet = &tactile_send_packet, + .receive_frame = &tactile_receive_frame }; - -} // namespace +} std::optional parse_frame(const CPFrame& frame) { if (!frame.valid || frame.data.size() < kMinimumFrameSize) { @@ -221,6 +213,7 @@ std::optional parse_frame(const CPFrame& frame) { if (bytes[0] != kStartByte) { return std::nullopt; } + if (bytes[size - 2] != kEndByteFirst || bytes[size - 1] != kEndByteSecond) { return std::nullopt; } @@ -233,7 +226,6 @@ std::optional parse_frame(const CPFrame& frame) { if (frame_length_from_payload(length) != size) { return std::nullopt; } - const std::uint8_t address = bytes[1]; const FunctionCode function = static_cast(bytes[2]); const std::size_t payload_offset = 4U; @@ -271,10 +263,6 @@ std::optional parse_matrix_size_payload(const TactileFrame& frame) { return size; } -std::optional parse_matrix_coordinate_payload(const TactileFrame& frame) { - return parse_matrix_size_payload(frame); -} - const CPCodec* tactile_codec() { return &kTactileCodec; } @@ -282,5 +270,4 @@ const CPCodec* tactile_codec() { void register_tactile_codec() { cpcodec_register(&kTactileCodec); } - -} // namespace ffmsep::tactile +} diff --git a/components/ffmsep/tactile/tacdec.h b/components/ffmsep/tactile/tacdec.hh similarity index 60% rename from components/ffmsep/tactile/tacdec.h rename to components/ffmsep/tactile/tacdec.hh index c6f01f4..7028b26 100644 --- a/components/ffmsep/tactile/tacdec.h +++ b/components/ffmsep/tactile/tacdec.hh @@ -1,29 +1,23 @@ -// -// High level helpers for the tactile sensor binary protocol. -// - #pragma once -#include "../cpdecoder.hh" - +#include "cpdecoder.hh" #include #include #include namespace ffmsep::tactile { - inline constexpr std::uint8_t kStartByte = 0x3A; inline constexpr std::uint8_t kEndByteFirst = 0x0D; inline constexpr std::uint8_t kEndByteSecond = 0x0A; enum class FunctionCode : std::uint8_t { - Unknown = 0x00, - ReadMatrix = 0x01, - ReadSingle = 0x02, - ReadTemperature = 0x03, - SetDeviceId = 0x51, - SetMatrixSize = 0x52, - CalibrationMode = 0x53 + Unknown = 0x00, + ReadMatrix = 0x01, + ReadSingle = 0x02, + ReadTemperature = 0x03, + SetDeviceId = 0x51, + SetMatrixSize = 0x52, + CalibrationMode = 0x53, }; struct MatrixSize { @@ -32,18 +26,17 @@ struct MatrixSize { }; struct TactileFrame { - std::uint8_t device_address = 0; - FunctionCode function = FunctionCode::Unknown; - std::uint8_t data_length = 0; + std::uint8_t device_address = 0; + FunctionCode function = FunctionCode::Unknown; + std::uint8_t data_length = 0; std::vector payload; }; std::optional parse_frame(const CPFrame& frame); std::vector parse_pressure_values(const TactileFrame& frame); std::optional parse_matrix_size_payload(const TactileFrame& frame); -std::optional parse_matrix_coordinate_payload(const TactileFrame& frame); +std::optional parse_patrix_coordinate_payload(const TactileFrame& frame); const CPCodec* tactile_codec(); void register_tactile_codec(); - -} // namespace ffmsep::tactile +} \ No newline at end of file diff --git a/components/view.cc b/components/view.cc index 369ac51..7bd6f44 100644 --- a/components/view.cc +++ b/components/view.cc @@ -70,24 +70,17 @@ static auto ComConfigComponent(ThemeManager& manager, auto&& callback) { slpro::LeadingIcon { material::icon::kArrowDropDown, material::regular::font}, slpro::IndexChanged {[&](auto& self){ qDebug() << self.currentIndex();}}, slpro::LeadingText {"Baud"}, - // slpro::MutableItems {select_baud_context}, MutableForward { slpro::SelectItems {}, select_baud_context, } }, - // lnpro::Item { - // // slpro::ThemeManager {manager}, - // // slpro::LeadingIcon {material::icon::kArrowDropDown, material::regular::font}, - // // slpro::IndexChanged {} - // // } lnpro::SpacingItem {20}, lnpro::Item { ibpro::ThemeManager {manager}, ibpro::FixedSize {40, 40}, ibpro::Color { IconButton::Color::TONAL }, ibpro::Font { material::kRoundSmallFont }, - // ibpro::FontIcon { material::icon::kFavorite }, ibpro::FontIcon { material::icon::kAddLink }, ibpro::Clickable {[slogen_context] { constexpr auto random_slogen = [] { @@ -113,15 +106,14 @@ static auto ComConfigComponent(ThemeManager& manager, auto&& callback) { ibpro::Font { material::kRoundSmallFont }, ibpro::FontIcon { material::icon::kRefresh }, ibpro::Clickable {[select_baud_context] { - // 定义两组不同的选项 + static constexpr auto options_group1 = std::array { "第一组选项1", "第一组选项2", "第一组选项3" }; static constexpr auto options_group2 = std::array { "第二组选项A", "第二组选项B", "第二组选项C", "第二组选项D" }; - - // 随机选择一组选项 + static std::random_device rd; static std::mt19937 gen(rd()); std::uniform_int_distribution<> dist(0, 1); @@ -136,9 +128,6 @@ static auto ComConfigComponent(ThemeManager& manager, auto&& callback) { new_options << QString::fromUtf8(option); } } - qDebug() << new_options; - - // 更新选项列表,MatSelect会自动刷新 *select_baud_context = new_options; }}, }, diff --git a/dlog/dlog.cc b/dlog/dlog.cc new file mode 100644 index 0000000..631bc62 --- /dev/null +++ b/dlog/dlog.cc @@ -0,0 +1,40 @@ +// +// Created by Lenn on 2025/10/23. +// + + +#include "dlog.hh" + +spdlog::level::level_enum DLog::level_ = spdlog::level::info; + +void DLog::set_level(DLogLevel level) { + printf("SetLevel log_level:%d\n", level); + fflush(stdout); + switch (level) + { + case DLogLevel::TRACE: + level_ = spdlog::level::trace; + break; + case DLogLevel::DEBUG: + level_ = spdlog::level::debug; + break; + case DLogLevel::INFO: + level_ = spdlog::level::info; + break; + case DLogLevel::WARN: + level_ = spdlog::level::warn; + break; + case DLogLevel::ERR: + level_ = spdlog::level::err; + break; + case DLogLevel::CRITICAL: + level_ = spdlog::level::critical; + break; + case DLogLevel::OFF: + level_ = spdlog::level::off; + break; + default: + level_ = spdlog::level::trace; + break; + } +} \ No newline at end of file diff --git a/dlog/dlog.hh b/dlog/dlog.hh new file mode 100644 index 0000000..bfedd75 --- /dev/null +++ b/dlog/dlog.hh @@ -0,0 +1,81 @@ +// +// Created by Lenn on 2025/10/23. +// + +#ifndef TOUCHSENSOR_DLOG_HH +#define TOUCHSENSOR_DLOG_HH + +#include +#include +#include +#include +#include +#include + +#ifndef SPDLOG_TRACE_ON +#define SPDLOG_TRACE_ON +#endif + +#ifndef SPDLOG_DEBUG_ON +#define SPDLOG_DEBUG_ON +#endif + +// #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE + +enum class DLogLevel { + TRACE, + DEBUG, + INFO, + WARN, + ERR, + CRITICAL, + OFF +}; + +class DLog{ + +public: + static DLog* get_instance() { + static DLog dlogger; + return &dlogger; + } + + std::shared_ptr get_logger() { + return log_; + } + + static void set_level(DLogLevel level); +private: + DLog() { + std::vector sinks; + auto consolesink = std::make_shared(); + consolesink->set_level(level_); + consolesink->set_pattern("[%Y-%m-%d %H:%M:%S.%e][thread %t][%@,%!][%l] : %v"); + sinks.push_back(consolesink); + + auto dailysink = std::make_shared("logs/daily.log", 23, 59); + dailysink->set_level(level_); + dailysink->set_pattern("[%Y-%m-%d %H:%M:%S.%e][thread %t][%@,%!][%l] : %v"); + sinks.push_back(dailysink); + + log_ = std::make_shared("both", std::begin(sinks), std::end(sinks)); + spdlog::register_logger(log_); + // spdlog::flush_every(std::chrono::seconds(1)); + spdlog::flush_every(std::chrono::seconds(1)); + } + + ~DLog() = default; + std::shared_ptr log_; + static spdlog::level::level_enum level_; +}; + + +#define LogTrace(...) SPDLOG_LOGGER_CALL(DLog::get_instance()->get_logger().get(), spdlog::level::trace, __VA_ARGS__); +#define LogDebug(...) SPDLOG_LOGGER_CALL(DLog::get_instance()->get_logger().get(), spdlog::level::debug, __VA_ARGS__); +#define LogInfo(...) SPDLOG_LOGGER_CALL(DLog::get_instance()->get_logger().get(), spdlog::level::info, __VA_ARGS__); +#define LogWarn(...) SPDLOG_LOGGER_CALL(DLog::get_instance()->get_logger().get(), spdlog::level::warn, __VA_ARGS__); +#define LogErr(...) SPDLOG_LOGGER_CALL(DLog::get_instance()->get_logger().get(), spdlog::level::err, __VA_ARGS__); +#define LogCritical(...) SPDLOG_LOGGER_CALL(DLog::get_instance()->get_logger().get(), spdlog::level::critical, __VA_ARGS__); +#define LogOff(...) SPDLOG_LOGGER_CALL(DLog::get_instance()->get_logger().get(), spdlog::level::off, __VA_ARGS__); + +#endif //TOUCHSENSOR_DLOG_HH diff --git a/examples/cpstream_demo.cc b/examples/cpstream_demo.cc new file mode 100644 index 0000000..8603a17 --- /dev/null +++ b/examples/cpstream_demo.cc @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "components/ffmsep/cpstream_core.hh" +#include "components/ffmsep/tactile/tacdec.hh" + +using namespace std::chrono_literals; + +static volatile std::sig_atomic_t g_running = 1; + +void handle_sigint(int) { + g_running = 0; +} + +int main(int argc, char** argv) { + std::signal(SIGINT, handle_sigint); + + // Register tactile codec so the stream can resolve it by ID or name. + ffmsep::tactile::register_tactile_codec(); + + const auto ports = ffmsep::CPStreamCore::list_available_ports(); + const auto describe_ports = [&ports]() { + if (ports.empty()) { + std::cout << "No serial ports detected.\n"; + return; + } + std::cout << "Available serial ports:\n"; + for (const auto& p : ports) { + std::cout << " - " << p.port.c_str() << " (" << p.description.c_str() << ")\n"; + } + }; + + std::string port; + if (argc >= 2) { + const std::string requested = argv[1]; + if (ports.empty()) { + std::cout << "No serial ports detected, attempting requested '" << requested << "'.\n"; + port = requested; + } else { + const auto it = std::find_if(ports.begin(), ports.end(), + [&](const auto& info) { return info.port == requested; }); + if (it == ports.end()) { + std::cerr << "Requested port '" << requested << "' not found among detected ports.\n"; + describe_ports(); + return 1; + } + port = it->port; + } + } else { + describe_ports(); + if (!ports.empty()) { + port = ports.front().port; + std::cout << "Auto-selecting: " << port << "\n"; + } else { + std::cerr << "No serial ports found. Exiting.\n"; + return 1; + } + } + + ffmsep::CPStreamConfig cfg; + cfg.port = port; + cfg.baudrate = 115200; + cfg.codec_id = ffmsep::CPCodecID::Tactile; // resolve tactile decoder + cfg.read_chunk_size = 256; + cfg.packet_queue_capacity = 128; + cfg.frame_queue_capacity = 32; + + ffmsep::CPStreamCore core(cfg); + if (!core.open()) { + std::cerr << "Open failed: " << core.last_error() << "\n"; + return 2; + } + + // Optional: receive frames via callback + core.set_frame_callback([](const ffmsep::DecodedFrame& df) { + const auto maybe = ffmsep::tactile::parse_frame(df.frame); + if (!maybe) { + return; + } + const auto& tf = *maybe; + const auto pv = ffmsep::tactile::parse_pressure_values(tf); + std::cout << "Frame pts=" << df.pts + << " addr=" << int(tf.device_address) + << " func=" << int(static_cast(tf.function)) + << " len=" << int(tf.data_length) + << " pressures=" << pv.size() << "\n"; + }); + + if (!core.start()) { + std::cerr << "Start failed: " << core.last_error() << "\n"; + return 3; + } + + std::cout << "Streaming from " << port << ". Press Ctrl+C to stop...\n"; + + // Also demonstrate polling API (in case users don't want callbacks) + while (g_running) { + ffmsep::DecodedFrame df; + if (core.wait_for_frame(df, 200ms)) { + const auto maybe = ffmsep::tactile::parse_frame(df.frame); + if (maybe) { + const auto sz = ffmsep::tactile::parse_matrix_size_payload(*maybe); + if (sz) { + std::cout << " matrix=" << int(sz->long_edge) << "x" << int(sz->short_edge) << "\n"; + } + } + } + } + + core.stop(); + core.close(); + std::cout << "Stopped.\n"; + return 0; +} diff --git a/modern-qt/utility/painter-resource.hh b/modern-qt/utility/painter-resource.hh index f2ecedd..2e77ef9 100644 --- a/modern-qt/utility/painter-resource.hh +++ b/modern-qt/utility/painter-resource.hh @@ -16,7 +16,6 @@ struct PainterResource : public QPixmap { : QPixmap {} { const auto qurl = QUrl(QString::fromUtf8(url.data(), static_cast(url.size()))); if (is_filesystem_url(url) || is_qt_resource_url(url)) { - qDebug() << "[PainterResource] is_filesystem_url" << url; QPixmap::load(qurl.path()); } else if (is_network_url(url)) { download_resource_from_network(qurl, [](auto&) { }); diff --git a/serial/CMakeLists.txt b/serial/CMakeLists.txt deleted file mode 100644 index a55af33..0000000 --- a/serial/CMakeLists.txt +++ /dev/null @@ -1,51 +0,0 @@ -project(serial) - -if(APPLE) - find_library(IOKIT_LIBRARY IOKit) - find_library(FOUNDATION_LIBRARY Foundation) -endif() - -if(UNIX AND NOT APPLE) - # If Linux, add rt and pthread - set(rt_LIBRARIES rt) - set(pthread_LIBRARIES pthread) -endif () - -## Sources -set(serial_SRCS - src/serial.cc - include/serial/serial.h - include/serial/v8stdint.h -) - -if(APPLE) - # If OSX - list(APPEND serial_SRCS src/impl/unix.cc) - list(APPEND serial_SRCS src/impl/list_ports/list_ports_osx.cc) -elseif(UNIX) - # If unix - list(APPEND serial_SRCS src/impl/unix.cc) - list(APPEND serial_SRCS src/impl/list_ports/list_ports_linux.cc) -else() - # If windows - list(APPEND serial_SRCS src/impl/win.cc) - list(APPEND serial_SRCS src/impl/list_ports/list_ports_win.cc) -endif() - -## Add serial library -add_library(${PROJECT_NAME} ${serial_SRCS}) -if(APPLE) - target_link_libraries(${PROJECT_NAME} ${FOUNDATION_LIBRARY} ${IOKIT_LIBRARY}) -elseif(UNIX) - target_link_libraries(${PROJECT_NAME} rt pthread) -else() - target_link_libraries(${PROJECT_NAME} setupapi) -endif() - -set_target_properties(serial PROPERTIES - AUTOMOC OFF - AUTORCC OFF - AUTOUIC OFF -) -## Include headers -include_directories(include) \ No newline at end of file diff --git a/serial/examples/serial_example.cc b/serial/examples/serial_example.cc deleted file mode 100644 index a829635..0000000 --- a/serial/examples/serial_example.cc +++ /dev/null @@ -1,182 +0,0 @@ -/*** - * This example expects the serial port has a loopback on it. - * - * Alternatively, you could use an Arduino: - * - *
- *  void setup() {
- *    Serial.begin();
- *  }
- *
- *  void loop() {
- *    if (Serial.available()) {
- *      Serial.write(Serial.read());
- *    }
- *  }
- * 
- */ - -#include -#include -#include - -// OS Specific sleep -#ifdef _WIN32 -#include -#else -#include -#endif - -#include "serial/serial.h" - -using std::string; -using std::exception; -using std::cout; -using std::cerr; -using std::endl; -using std::vector; - -void my_sleep(unsigned long milliseconds) { -#ifdef _WIN32 - Sleep(milliseconds); // 100 ms -#else - usleep(milliseconds*1000); // 100 ms -#endif -} - -void enumerate_ports() -{ - vector devices_found = serial::list_ports(); - - vector::iterator iter = devices_found.begin(); - - while( iter != devices_found.end() ) - { - serial::PortInfo device = *iter++; - - printf( "(%s, %s, %s)\n", device.port.c_str(), device.description.c_str(), - device.hardware_id.c_str() ); - } -} - -void print_usage() -{ - cerr << "Usage: test_serial {-e|} "; - cerr << " [test string]" << endl; -} - -int run(int argc, char **argv) -{ - if(argc < 2) { - print_usage(); - return 0; - } - - // Argument 1 is the serial port or enumerate flag - string port(argv[1]); - - if( port == "-e" ) { - enumerate_ports(); - return 0; - } - else if( argc < 3 ) { - print_usage(); - return 1; - } - - // Argument 2 is the baudrate - unsigned long baud = 0; -#if defined(WIN32) && !defined(__MINGW32__) - sscanf_s(argv[2], "%lu", &baud); -#else - sscanf(argv[2], "%lu", &baud); -#endif - - // port, baudrate, timeout in milliseconds - serial::Serial my_serial(port, baud, serial::Timeout::simpleTimeout(1000)); - - cout << "Is the serial port open?"; - if(my_serial.isOpen()) - cout << " Yes." << endl; - else - cout << " No." << endl; - - // Get the Test string - int count = 0; - string test_string; - if (argc == 4) { - test_string = argv[3]; - } else { - test_string = "Testing."; - } - - // Test the timeout, there should be 1 second between prints - cout << "Timeout == 1000ms, asking for 1 more byte than written." << endl; - while (count < 10) { - size_t bytes_wrote = my_serial.write(test_string); - - string result = my_serial.read(test_string.length()+1); - - cout << "Iteration: " << count << ", Bytes written: "; - cout << bytes_wrote << ", Bytes read: "; - cout << result.length() << ", String read: " << result << endl; - - count += 1; - } - - // Test the timeout at 250ms - my_serial.setTimeout(serial::Timeout::max(), 250, 0, 250, 0); - count = 0; - cout << "Timeout == 250ms, asking for 1 more byte than written." << endl; - while (count < 10) { - size_t bytes_wrote = my_serial.write(test_string); - - string result = my_serial.read(test_string.length()+1); - - cout << "Iteration: " << count << ", Bytes written: "; - cout << bytes_wrote << ", Bytes read: "; - cout << result.length() << ", String read: " << result << endl; - - count += 1; - } - - // Test the timeout at 250ms, but asking exactly for what was written - count = 0; - cout << "Timeout == 250ms, asking for exactly what was written." << endl; - while (count < 10) { - size_t bytes_wrote = my_serial.write(test_string); - - string result = my_serial.read(test_string.length()); - - cout << "Iteration: " << count << ", Bytes written: "; - cout << bytes_wrote << ", Bytes read: "; - cout << result.length() << ", String read: " << result << endl; - - count += 1; - } - - // Test the timeout at 250ms, but asking for 1 less than what was written - count = 0; - cout << "Timeout == 250ms, asking for 1 less than was written." << endl; - while (count < 10) { - size_t bytes_wrote = my_serial.write(test_string); - - string result = my_serial.read(test_string.length()-1); - - cout << "Iteration: " << count << ", Bytes written: "; - cout << bytes_wrote << ", Bytes read: "; - cout << result.length() << ", String read: " << result << endl; - - count += 1; - } - - return 0; -} - -int main(int argc, char **argv) { - try { - return run(argc, argv); - } catch (exception &e) { - cerr << "Unhandled Exception: " << e.what() << endl; - } -} diff --git a/serial/include/serial/impl/unix.h b/serial/include/serial/impl/unix.h deleted file mode 100644 index 0fb38f2..0000000 --- a/serial/include/serial/impl/unix.h +++ /dev/null @@ -1,221 +0,0 @@ -/*! - * \file serial/impl/unix.h - * \author William Woodall - * \author John Harrison - * \version 0.1 - * - * \section LICENSE - * - * The MIT License - * - * Copyright (c) 2012 William Woodall, John Harrison - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * \section DESCRIPTION - * - * This provides a unix based pimpl for the Serial class. This implementation is - * based off termios.h and uses select for multiplexing the IO ports. - * - */ - -#if !defined(_WIN32) - -#ifndef SERIAL_IMPL_UNIX_H -#define SERIAL_IMPL_UNIX_H - -#include "serial/serial.h" - -#include - -namespace serial { - -using std::size_t; -using std::string; -using std::invalid_argument; - -using serial::SerialException; -using serial::IOException; - -class MillisecondTimer { -public: - MillisecondTimer(const uint32_t millis); - int64_t remaining(); - -private: - static timespec timespec_now(); - timespec expiry; -}; - -class serial::Serial::SerialImpl { -public: - SerialImpl (const string &port, - unsigned long baudrate, - bytesize_t bytesize, - parity_t parity, - stopbits_t stopbits, - flowcontrol_t flowcontrol); - - virtual ~SerialImpl (); - - void - open (); - - void - close (); - - bool - isOpen () const; - - size_t - available (); - - bool - waitReadable (uint32_t timeout); - - void - waitByteTimes (size_t count); - - size_t - read (uint8_t *buf, size_t size = 1); - - size_t - write (const uint8_t *data, size_t length); - - void - flush (); - - void - flushInput (); - - void - flushOutput (); - - void - sendBreak (int duration); - - void - setBreak (bool level); - - void - setRTS (bool level); - - void - setDTR (bool level); - - bool - waitForChange (); - - bool - getCTS (); - - bool - getDSR (); - - bool - getRI (); - - bool - getCD (); - - void - setPort (const string &port); - - string - getPort () const; - - void - setTimeout (Timeout &timeout); - - Timeout - getTimeout () const; - - void - setBaudrate (unsigned long baudrate); - - unsigned long - getBaudrate () const; - - void - setBytesize (bytesize_t bytesize); - - bytesize_t - getBytesize () const; - - void - setParity (parity_t parity); - - parity_t - getParity () const; - - void - setStopbits (stopbits_t stopbits); - - stopbits_t - getStopbits () const; - - void - setFlowcontrol (flowcontrol_t flowcontrol); - - flowcontrol_t - getFlowcontrol () const; - - void - readLock (); - - void - readUnlock (); - - void - writeLock (); - - void - writeUnlock (); - -protected: - void reconfigurePort (); - -private: - string port_; // Path to the file descriptor - int fd_; // The current file descriptor - - bool is_open_; - bool xonxoff_; - bool rtscts_; - - Timeout timeout_; // Timeout for read operations - unsigned long baudrate_; // Baudrate - uint32_t byte_time_ns_; // Nanoseconds to transmit/receive a single byte - - parity_t parity_; // Parity - bytesize_t bytesize_; // Size of the bytes - stopbits_t stopbits_; // Stop Bits - flowcontrol_t flowcontrol_; // Flow Control - - // Mutex used to lock the read functions - pthread_mutex_t read_mutex; - // Mutex used to lock the write functions - pthread_mutex_t write_mutex; -}; - -} - -#endif // SERIAL_IMPL_UNIX_H - -#endif // !defined(_WIN32) diff --git a/serial/include/serial/impl/win.h b/serial/include/serial/impl/win.h deleted file mode 100644 index 2c0c6cd..0000000 --- a/serial/include/serial/impl/win.h +++ /dev/null @@ -1,207 +0,0 @@ -/*! - * \file serial/impl/windows.h - * \author William Woodall - * \author John Harrison - * \version 0.1 - * - * \section LICENSE - * - * The MIT License - * - * Copyright (c) 2012 William Woodall, John Harrison - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * \section DESCRIPTION - * - * This provides a windows implementation of the Serial class interface. - * - */ - -#if defined(_WIN32) - -#ifndef SERIAL_IMPL_WINDOWS_H -#define SERIAL_IMPL_WINDOWS_H - -#include "serial/serial.h" - -#include "windows.h" - -namespace serial { - -using std::string; -using std::wstring; -using std::invalid_argument; - -using serial::SerialException; -using serial::IOException; - -class serial::Serial::SerialImpl { -public: - SerialImpl (const string &port, - unsigned long baudrate, - bytesize_t bytesize, - parity_t parity, - stopbits_t stopbits, - flowcontrol_t flowcontrol); - - virtual ~SerialImpl (); - - void - open (); - - void - close (); - - bool - isOpen () const; - - size_t - available (); - - bool - waitReadable (uint32_t timeout); - - void - waitByteTimes (size_t count); - - size_t - read (uint8_t *buf, size_t size = 1); - - size_t - write (const uint8_t *data, size_t length); - - void - flush (); - - void - flushInput (); - - void - flushOutput (); - - void - sendBreak (int duration); - - void - setBreak (bool level); - - void - setRTS (bool level); - - void - setDTR (bool level); - - bool - waitForChange (); - - bool - getCTS (); - - bool - getDSR (); - - bool - getRI (); - - bool - getCD (); - - void - setPort (const string &port); - - string - getPort () const; - - void - setTimeout (Timeout &timeout); - - Timeout - getTimeout () const; - - void - setBaudrate (unsigned long baudrate); - - unsigned long - getBaudrate () const; - - void - setBytesize (bytesize_t bytesize); - - bytesize_t - getBytesize () const; - - void - setParity (parity_t parity); - - parity_t - getParity () const; - - void - setStopbits (stopbits_t stopbits); - - stopbits_t - getStopbits () const; - - void - setFlowcontrol (flowcontrol_t flowcontrol); - - flowcontrol_t - getFlowcontrol () const; - - void - readLock (); - - void - readUnlock (); - - void - writeLock (); - - void - writeUnlock (); - -protected: - void reconfigurePort (); - -private: - wstring port_; // Path to the file descriptor - HANDLE fd_; - - bool is_open_; - - Timeout timeout_; // Timeout for read operations - unsigned long baudrate_; // Baudrate - - parity_t parity_; // Parity - bytesize_t bytesize_; // Size of the bytes - stopbits_t stopbits_; // Stop Bits - flowcontrol_t flowcontrol_; // Flow Control - - // Mutex used to lock the read functions - HANDLE read_mutex; - // Mutex used to lock the write functions - HANDLE write_mutex; -}; - -} - -#endif // SERIAL_IMPL_WINDOWS_H - -#endif // if defined(_WIN32) diff --git a/serial/include/serial/serial.h b/serial/include/serial/serial.h deleted file mode 100644 index a165785..0000000 --- a/serial/include/serial/serial.h +++ /dev/null @@ -1,775 +0,0 @@ -/*! - * \file serial/serial.h - * \author William Woodall - * \author John Harrison - * \version 0.1 - * - * \section LICENSE - * - * The MIT License - * - * Copyright (c) 2012 William Woodall - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * \section DESCRIPTION - * - * This provides a cross platform interface for interacting with Serial Ports. - */ - -#ifndef SERIAL_H -#define SERIAL_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#define THROW(exceptionClass, message) throw exceptionClass(__FILE__, \ -__LINE__, (message) ) - -namespace serial { - -/*! - * Enumeration defines the possible bytesizes for the serial port. - */ -typedef enum { - fivebits = 5, - sixbits = 6, - sevenbits = 7, - eightbits = 8 -} bytesize_t; - -/*! - * Enumeration defines the possible parity types for the serial port. - */ -typedef enum { - parity_none = 0, - parity_odd = 1, - parity_even = 2, - parity_mark = 3, - parity_space = 4 -} parity_t; - -/*! - * Enumeration defines the possible stopbit types for the serial port. - */ -typedef enum { - stopbits_one = 1, - stopbits_two = 2, - stopbits_one_point_five -} stopbits_t; - -/*! - * Enumeration defines the possible flowcontrol types for the serial port. - */ -typedef enum { - flowcontrol_none = 0, - flowcontrol_software, - flowcontrol_hardware -} flowcontrol_t; - -/*! - * Structure for setting the timeout of the serial port, times are - * in milliseconds. - * - * In order to disable the interbyte timeout, set it to Timeout::max(). - */ -struct Timeout { -#ifdef max -# undef max -#endif - static uint32_t max() {return std::numeric_limits::max();} - /*! - * Convenience function to generate Timeout structs using a - * single absolute timeout. - * - * \param timeout A long that defines the time in milliseconds until a - * timeout occurs after a call to read or write is made. - * - * \return Timeout struct that represents this simple timeout provided. - */ - static Timeout simpleTimeout(uint32_t timeout) { - return Timeout(max(), timeout, 0, timeout, 0); - } - - /*! Number of milliseconds between bytes received to timeout on. */ - uint32_t inter_byte_timeout; - /*! A constant number of milliseconds to wait after calling read. */ - uint32_t read_timeout_constant; - /*! A multiplier against the number of requested bytes to wait after - * calling read. - */ - uint32_t read_timeout_multiplier; - /*! A constant number of milliseconds to wait after calling write. */ - uint32_t write_timeout_constant; - /*! A multiplier against the number of requested bytes to wait after - * calling write. - */ - uint32_t write_timeout_multiplier; - - explicit Timeout (uint32_t inter_byte_timeout_=0, - uint32_t read_timeout_constant_=0, - uint32_t read_timeout_multiplier_=0, - uint32_t write_timeout_constant_=0, - uint32_t write_timeout_multiplier_=0) - : inter_byte_timeout(inter_byte_timeout_), - read_timeout_constant(read_timeout_constant_), - read_timeout_multiplier(read_timeout_multiplier_), - write_timeout_constant(write_timeout_constant_), - write_timeout_multiplier(write_timeout_multiplier_) - {} -}; - -/*! - * Class that provides a portable serial port interface. - */ -class Serial { -public: - /*! - * Creates a Serial object and opens the port if a port is specified, - * otherwise it remains closed until serial::Serial::open is called. - * - * \param port A std::string containing the address of the serial port, - * which would be something like 'COM1' on Windows and '/dev/ttyS0' - * on Linux. - * - * \param baudrate An unsigned 32-bit integer that represents the baudrate - * - * \param timeout A serial::Timeout struct that defines the timeout - * conditions for the serial port. \see serial::Timeout - * - * \param bytesize Size of each byte in the serial transmission of data, - * default is eightbits, possible values are: fivebits, sixbits, sevenbits, - * eightbits - * - * \param parity Method of parity, default is parity_none, possible values - * are: parity_none, parity_odd, parity_even - * - * \param stopbits Number of stop bits used, default is stopbits_one, - * possible values are: stopbits_one, stopbits_one_point_five, stopbits_two - * - * \param flowcontrol Type of flowcontrol used, default is - * flowcontrol_none, possible values are: flowcontrol_none, - * flowcontrol_software, flowcontrol_hardware - * - * \throw serial::PortNotOpenedException - * \throw serial::IOException - * \throw std::invalid_argument - */ - Serial (const std::string &port = "", - uint32_t baudrate = 9600, - Timeout timeout = Timeout(), - bytesize_t bytesize = eightbits, - parity_t parity = parity_none, - stopbits_t stopbits = stopbits_one, - flowcontrol_t flowcontrol = flowcontrol_none); - - /*! Destructor */ - virtual ~Serial (); - - /*! - * Opens the serial port as long as the port is set and the port isn't - * already open. - * - * If the port is provided to the constructor then an explicit call to open - * is not needed. - * - * \see Serial::Serial - * - * \throw std::invalid_argument - * \throw serial::SerialException - * \throw serial::IOException - */ - void - open (); - - /*! Gets the open status of the serial port. - * - * \return Returns true if the port is open, false otherwise. - */ - bool - isOpen () const; - - /*! Closes the serial port. */ - void - close (); - - /*! Return the number of characters in the buffer. */ - size_t - available (); - - /*! Block until there is serial data to read or read_timeout_constant - * number of milliseconds have elapsed. The return value is true when - * the function exits with the port in a readable state, false otherwise - * (due to timeout or select interruption). */ - bool - waitReadable (); - - /*! Block for a period of time corresponding to the transmission time of - * count characters at present serial settings. This may be used in con- - * junction with waitReadable to read larger blocks of data from the - * port. */ - void - waitByteTimes (size_t count); - - /*! Read a given amount of bytes from the serial port into a given buffer. - * - * The read function will return in one of three cases: - * * The number of requested bytes was read. - * * In this case the number of bytes requested will match the size_t - * returned by read. - * * A timeout occurred, in this case the number of bytes read will not - * match the amount requested, but no exception will be thrown. One of - * two possible timeouts occurred: - * * The inter byte timeout expired, this means that number of - * milliseconds elapsed between receiving bytes from the serial port - * exceeded the inter byte timeout. - * * The total timeout expired, which is calculated by multiplying the - * read timeout multiplier by the number of requested bytes and then - * added to the read timeout constant. If that total number of - * milliseconds elapses after the initial call to read a timeout will - * occur. - * * An exception occurred, in this case an actual exception will be thrown. - * - * \param buffer An uint8_t array of at least the requested size. - * \param size A size_t defining how many bytes to be read. - * - * \return A size_t representing the number of bytes read as a result of the - * call to read. - * - * \throw serial::PortNotOpenedException - * \throw serial::SerialException - */ - size_t - read (uint8_t *buffer, size_t size); - - /*! Read a given amount of bytes from the serial port into a give buffer. - * - * \param buffer A reference to a std::vector of uint8_t. - * \param size A size_t defining how many bytes to be read. - * - * \return A size_t representing the number of bytes read as a result of the - * call to read. - * - * \throw serial::PortNotOpenedException - * \throw serial::SerialException - */ - size_t - read (std::vector &buffer, size_t size = 1); - - /*! Read a given amount of bytes from the serial port into a give buffer. - * - * \param buffer A reference to a std::string. - * \param size A size_t defining how many bytes to be read. - * - * \return A size_t representing the number of bytes read as a result of the - * call to read. - * - * \throw serial::PortNotOpenedException - * \throw serial::SerialException - */ - size_t - read (std::string &buffer, size_t size = 1); - - /*! Read a given amount of bytes from the serial port and return a string - * containing the data. - * - * \param size A size_t defining how many bytes to be read. - * - * \return A std::string containing the data read from the port. - * - * \throw serial::PortNotOpenedException - * \throw serial::SerialException - */ - std::string - read (size_t size = 1); - - /*! Reads in a line or until a given delimiter has been processed. - * - * Reads from the serial port until a single line has been read. - * - * \param buffer A std::string reference used to store the data. - * \param size A maximum length of a line, defaults to 65536 (2^16) - * \param eol A string to match against for the EOL. - * - * \return A size_t representing the number of bytes read. - * - * \throw serial::PortNotOpenedException - * \throw serial::SerialException - */ - size_t - readline (std::string &buffer, size_t size = 65536, std::string eol = "\n"); - - /*! Reads in a line or until a given delimiter has been processed. - * - * Reads from the serial port until a single line has been read. - * - * \param size A maximum length of a line, defaults to 65536 (2^16) - * \param eol A string to match against for the EOL. - * - * \return A std::string containing the line. - * - * \throw serial::PortNotOpenedException - * \throw serial::SerialException - */ - std::string - readline (size_t size = 65536, std::string eol = "\n"); - - /*! Reads in multiple lines until the serial port times out. - * - * This requires a timeout > 0 before it can be run. It will read until a - * timeout occurs and return a list of strings. - * - * \param size A maximum length of combined lines, defaults to 65536 (2^16) - * - * \param eol A string to match against for the EOL. - * - * \return A vector containing the lines. - * - * \throw serial::PortNotOpenedException - * \throw serial::SerialException - */ - std::vector - readlines (size_t size = 65536, std::string eol = "\n"); - - /*! Write a string to the serial port. - * - * \param data A const reference containing the data to be written - * to the serial port. - * - * \param size A size_t that indicates how many bytes should be written from - * the given data buffer. - * - * \return A size_t representing the number of bytes actually written to - * the serial port. - * - * \throw serial::PortNotOpenedException - * \throw serial::SerialException - * \throw serial::IOException - */ - size_t - write (const uint8_t *data, size_t size); - - /*! Write a string to the serial port. - * - * \param data A const reference containing the data to be written - * to the serial port. - * - * \return A size_t representing the number of bytes actually written to - * the serial port. - * - * \throw serial::PortNotOpenedException - * \throw serial::SerialException - * \throw serial::IOException - */ - size_t - write (const std::vector &data); - - /*! Write a string to the serial port. - * - * \param data A const reference containing the data to be written - * to the serial port. - * - * \return A size_t representing the number of bytes actually written to - * the serial port. - * - * \throw serial::PortNotOpenedException - * \throw serial::SerialException - * \throw serial::IOException - */ - size_t - write (const std::string &data); - - /*! Sets the serial port identifier. - * - * \param port A const std::string reference containing the address of the - * serial port, which would be something like 'COM1' on Windows and - * '/dev/ttyS0' on Linux. - * - * \throw std::invalid_argument - */ - void - setPort (const std::string &port); - - /*! Gets the serial port identifier. - * - * \see Serial::setPort - * - * \throw std::invalid_argument - */ - std::string - getPort () const; - - /*! Sets the timeout for reads and writes using the Timeout struct. - * - * There are two timeout conditions described here: - * * The inter byte timeout: - * * The inter_byte_timeout component of serial::Timeout defines the - * maximum amount of time, in milliseconds, between receiving bytes on - * the serial port that can pass before a timeout occurs. Setting this - * to zero will prevent inter byte timeouts from occurring. - * * Total time timeout: - * * The constant and multiplier component of this timeout condition, - * for both read and write, are defined in serial::Timeout. This - * timeout occurs if the total time since the read or write call was - * made exceeds the specified time in milliseconds. - * * The limit is defined by multiplying the multiplier component by the - * number of requested bytes and adding that product to the constant - * component. In this way if you want a read call, for example, to - * timeout after exactly one second regardless of the number of bytes - * you asked for then set the read_timeout_constant component of - * serial::Timeout to 1000 and the read_timeout_multiplier to zero. - * This timeout condition can be used in conjunction with the inter - * byte timeout condition with out any problems, timeout will simply - * occur when one of the two timeout conditions is met. This allows - * users to have maximum control over the trade-off between - * responsiveness and efficiency. - * - * Read and write functions will return in one of three cases. When the - * reading or writing is complete, when a timeout occurs, or when an - * exception occurs. - * - * A timeout of 0 enables non-blocking mode. - * - * \param timeout A serial::Timeout struct containing the inter byte - * timeout, and the read and write timeout constants and multipliers. - * - * \see serial::Timeout - */ - void - setTimeout (Timeout &timeout); - - /*! Sets the timeout for reads and writes. */ - void - setTimeout (uint32_t inter_byte_timeout, uint32_t read_timeout_constant, - uint32_t read_timeout_multiplier, uint32_t write_timeout_constant, - uint32_t write_timeout_multiplier) - { - Timeout timeout(inter_byte_timeout, read_timeout_constant, - read_timeout_multiplier, write_timeout_constant, - write_timeout_multiplier); - return setTimeout(timeout); - } - - /*! Gets the timeout for reads in seconds. - * - * \return A Timeout struct containing the inter_byte_timeout, and read - * and write timeout constants and multipliers. - * - * \see Serial::setTimeout - */ - Timeout - getTimeout () const; - - /*! Sets the baudrate for the serial port. - * - * Possible baudrates depends on the system but some safe baudrates include: - * 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 56000, - * 57600, 115200 - * Some other baudrates that are supported by some comports: - * 128000, 153600, 230400, 256000, 460800, 500000, 921600 - * - * \param baudrate An integer that sets the baud rate for the serial port. - * - * \throw std::invalid_argument - */ - void - setBaudrate (uint32_t baudrate); - - /*! Gets the baudrate for the serial port. - * - * \return An integer that sets the baud rate for the serial port. - * - * \see Serial::setBaudrate - * - * \throw std::invalid_argument - */ - uint32_t - getBaudrate () const; - - /*! Sets the bytesize for the serial port. - * - * \param bytesize Size of each byte in the serial transmission of data, - * default is eightbits, possible values are: fivebits, sixbits, sevenbits, - * eightbits - * - * \throw std::invalid_argument - */ - void - setBytesize (bytesize_t bytesize); - - /*! Gets the bytesize for the serial port. - * - * \see Serial::setBytesize - * - * \throw std::invalid_argument - */ - bytesize_t - getBytesize () const; - - /*! Sets the parity for the serial port. - * - * \param parity Method of parity, default is parity_none, possible values - * are: parity_none, parity_odd, parity_even - * - * \throw std::invalid_argument - */ - void - setParity (parity_t parity); - - /*! Gets the parity for the serial port. - * - * \see Serial::setParity - * - * \throw std::invalid_argument - */ - parity_t - getParity () const; - - /*! Sets the stopbits for the serial port. - * - * \param stopbits Number of stop bits used, default is stopbits_one, - * possible values are: stopbits_one, stopbits_one_point_five, stopbits_two - * - * \throw std::invalid_argument - */ - void - setStopbits (stopbits_t stopbits); - - /*! Gets the stopbits for the serial port. - * - * \see Serial::setStopbits - * - * \throw std::invalid_argument - */ - stopbits_t - getStopbits () const; - - /*! Sets the flow control for the serial port. - * - * \param flowcontrol Type of flowcontrol used, default is flowcontrol_none, - * possible values are: flowcontrol_none, flowcontrol_software, - * flowcontrol_hardware - * - * \throw std::invalid_argument - */ - void - setFlowcontrol (flowcontrol_t flowcontrol); - - /*! Gets the flow control for the serial port. - * - * \see Serial::setFlowcontrol - * - * \throw std::invalid_argument - */ - flowcontrol_t - getFlowcontrol () const; - - /*! Flush the input and output buffers */ - void - flush (); - - /*! Flush only the input buffer */ - void - flushInput (); - - /*! Flush only the output buffer */ - void - flushOutput (); - - /*! Sends the RS-232 break signal. See tcsendbreak(3). */ - void - sendBreak (int duration); - - /*! Set the break condition to a given level. Defaults to true. */ - void - setBreak (bool level = true); - - /*! Set the RTS handshaking line to the given level. Defaults to true. */ - void - setRTS (bool level = true); - - /*! Set the DTR handshaking line to the given level. Defaults to true. */ - void - setDTR (bool level = true); - - /*! - * Blocks until CTS, DSR, RI, CD changes or something interrupts it. - * - * Can throw an exception if an error occurs while waiting. - * You can check the status of CTS, DSR, RI, and CD once this returns. - * Uses TIOCMIWAIT via ioctl if available (mostly only on Linux) with a - * resolution of less than +-1ms and as good as +-0.2ms. Otherwise a - * polling method is used which can give +-2ms. - * - * \return Returns true if one of the lines changed, false if something else - * occurred. - * - * \throw SerialException - */ - bool - waitForChange (); - - /*! Returns the current status of the CTS line. */ - bool - getCTS (); - - /*! Returns the current status of the DSR line. */ - bool - getDSR (); - - /*! Returns the current status of the RI line. */ - bool - getRI (); - - /*! Returns the current status of the CD line. */ - bool - getCD (); - -private: - // Disable copy constructors - Serial(const Serial&); - Serial& operator=(const Serial&); - - // Pimpl idiom, d_pointer - class SerialImpl; - SerialImpl *pimpl_; - - // Scoped Lock Classes - class ScopedReadLock; - class ScopedWriteLock; - - // Read common function - size_t - read_ (uint8_t *buffer, size_t size); - // Write common function - size_t - write_ (const uint8_t *data, size_t length); - -}; - -class SerialException : public std::exception -{ - // Disable copy constructors - SerialException& operator=(const SerialException&); - std::string e_what_; -public: - SerialException (const char *description) { - std::stringstream ss; - ss << "SerialException " << description << " failed."; - e_what_ = ss.str(); - } - SerialException (const SerialException& other) : e_what_(other.e_what_) {} - virtual ~SerialException() throw() {} - virtual const char* what () const throw () { - return e_what_.c_str(); - } -}; - -class IOException : public std::exception -{ - // Disable copy constructors - IOException& operator=(const IOException&); - std::string file_; - int line_; - std::string e_what_; - int errno_; -public: - explicit IOException (std::string file, int line, int errnum) - : file_(file), line_(line), errno_(errnum) { - std::stringstream ss; -#if defined(_WIN32) && !defined(__MINGW32__) - char error_str [1024]; - strerror_s(error_str, 1024, errnum); -#else - char * error_str = strerror(errnum); -#endif - ss << "IO Exception (" << errno_ << "): " << error_str; - ss << ", file " << file_ << ", line " << line_ << "."; - e_what_ = ss.str(); - } - explicit IOException (std::string file, int line, const char * description) - : file_(file), line_(line), errno_(0) { - std::stringstream ss; - ss << "IO Exception: " << description; - ss << ", file " << file_ << ", line " << line_ << "."; - e_what_ = ss.str(); - } - virtual ~IOException() throw() {} - IOException (const IOException& other) : line_(other.line_), e_what_(other.e_what_), errno_(other.errno_) {} - - int getErrorNumber () const { return errno_; } - - virtual const char* what () const throw () { - return e_what_.c_str(); - } -}; - -class PortNotOpenedException : public std::exception -{ - // Disable copy constructors - const PortNotOpenedException& operator=(PortNotOpenedException); - std::string e_what_; -public: - PortNotOpenedException (const char * description) { - std::stringstream ss; - ss << "PortNotOpenedException " << description << " failed."; - e_what_ = ss.str(); - } - PortNotOpenedException (const PortNotOpenedException& other) : e_what_(other.e_what_) {} - virtual ~PortNotOpenedException() throw() {} - virtual const char* what () const throw () { - return e_what_.c_str(); - } -}; - -/*! - * Structure that describes a serial device. - */ -struct PortInfo { - - /*! Address of the serial port (this can be passed to the constructor of Serial). */ - std::string port; - - /*! Human readable description of serial device if available. */ - std::string description; - - /*! Hardware ID (e.g. VID:PID of USB serial devices) or "n/a" if not available. */ - std::string hardware_id; - -}; - -/* Lists the serial ports available on the system - * - * Returns a vector of available serial ports, each represented - * by a serial::PortInfo data structure: - * - * \return vector of serial::PortInfo. - */ -std::vector -list_ports(); - -} // namespace serial - -#endif diff --git a/serial/include/serial/v8stdint.h b/serial/include/serial/v8stdint.h deleted file mode 100644 index f3be96b..0000000 --- a/serial/include/serial/v8stdint.h +++ /dev/null @@ -1,57 +0,0 @@ -// This header is from the v8 google project: -// http://code.google.com/p/v8/source/browse/trunk/include/v8stdint.h - -// Copyright 2012 the V8 project authors. All rights reserved. -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Load definitions of standard types. - -#ifndef V8STDINT_H_ -#define V8STDINT_H_ - -#include -#include - -#if defined(_WIN32) && !defined(__MINGW32__) - -typedef signed char int8_t; -typedef unsigned char uint8_t; -typedef short int16_t; // NOLINT -typedef unsigned short uint16_t; // NOLINT -typedef int int32_t; -typedef unsigned int uint32_t; -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -// intptr_t and friends are defined in crtdefs.h through stdio.h. - -#else - -#include - -#endif - -#endif // V8STDINT_H_ diff --git a/serial/src/impl/list_ports/list_ports_linux.cc b/serial/src/impl/list_ports/list_ports_linux.cc deleted file mode 100644 index db2afb2..0000000 --- a/serial/src/impl/list_ports/list_ports_linux.cc +++ /dev/null @@ -1,336 +0,0 @@ -#if defined(__linux__) - -/* - * Copyright (c) 2014 Craig Lilley - * This software is made available under the terms of the MIT licence. - * A copy of the licence can be obtained from: - * http://opensource.org/licenses/MIT - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "serial/serial.h" - -using serial::PortInfo; -using std::istringstream; -using std::ifstream; -using std::getline; -using std::vector; -using std::string; -using std::cout; -using std::endl; - -static vector glob(const vector& patterns); -static string basename(const string& path); -static string dirname(const string& path); -static bool path_exists(const string& path); -static string realpath(const string& path); -static string usb_sysfs_friendly_name(const string& sys_usb_path); -static vector get_sysfs_info(const string& device_path); -static string read_line(const string& file); -static string usb_sysfs_hw_string(const string& sysfs_path); -static string format(const char* format, ...); - -vector -glob(const vector& patterns) -{ - vector paths_found; - - if(patterns.size() == 0) - return paths_found; - - glob_t glob_results; - - int glob_retval = glob(patterns[0].c_str(), 0, NULL, &glob_results); - - vector::const_iterator iter = patterns.begin(); - - while(++iter != patterns.end()) - { - glob_retval = glob(iter->c_str(), GLOB_APPEND, NULL, &glob_results); - } - - for(int path_index = 0; path_index < glob_results.gl_pathc; path_index++) - { - paths_found.push_back(glob_results.gl_pathv[path_index]); - } - - globfree(&glob_results); - - return paths_found; -} - -string -basename(const string& path) -{ - size_t pos = path.rfind("/"); - - if(pos == std::string::npos) - return path; - - return string(path, pos+1, string::npos); -} - -string -dirname(const string& path) -{ - size_t pos = path.rfind("/"); - - if(pos == std::string::npos) - return path; - else if(pos == 0) - return "/"; - - return string(path, 0, pos); -} - -bool -path_exists(const string& path) -{ - struct stat sb; - - if( stat(path.c_str(), &sb ) == 0 ) - return true; - - return false; -} - -string -realpath(const string& path) -{ - char* real_path = realpath(path.c_str(), NULL); - - string result; - - if(real_path != NULL) - { - result = real_path; - - free(real_path); - } - - return result; -} - -string -usb_sysfs_friendly_name(const string& sys_usb_path) -{ - unsigned int device_number = 0; - - istringstream( read_line(sys_usb_path + "/devnum") ) >> device_number; - - string manufacturer = read_line( sys_usb_path + "/manufacturer" ); - - string product = read_line( sys_usb_path + "/product" ); - - string serial = read_line( sys_usb_path + "/serial" ); - - if( manufacturer.empty() && product.empty() && serial.empty() ) - return ""; - - return format("%s %s %s", manufacturer.c_str(), product.c_str(), serial.c_str() ); -} - -vector -get_sysfs_info(const string& device_path) -{ - string device_name = basename( device_path ); - - string friendly_name; - - string hardware_id; - - string sys_device_path = format( "/sys/class/tty/%s/device", device_name.c_str() ); - - if( device_name.compare(0,6,"ttyUSB") == 0 ) - { - sys_device_path = dirname( dirname( realpath( sys_device_path ) ) ); - - if( path_exists( sys_device_path ) ) - { - friendly_name = usb_sysfs_friendly_name( sys_device_path ); - - hardware_id = usb_sysfs_hw_string( sys_device_path ); - } - } - else if( device_name.compare(0,6,"ttyACM") == 0 ) - { - sys_device_path = dirname( realpath( sys_device_path ) ); - - if( path_exists( sys_device_path ) ) - { - friendly_name = usb_sysfs_friendly_name( sys_device_path ); - - hardware_id = usb_sysfs_hw_string( sys_device_path ); - } - } - else - { - // Try to read ID string of PCI device - - string sys_id_path = sys_device_path + "/id"; - - if( path_exists( sys_id_path ) ) - hardware_id = read_line( sys_id_path ); - } - - if( friendly_name.empty() ) - friendly_name = device_name; - - if( hardware_id.empty() ) - hardware_id = "n/a"; - - vector result; - result.push_back(friendly_name); - result.push_back(hardware_id); - - return result; -} - -string -read_line(const string& file) -{ - ifstream ifs(file.c_str(), ifstream::in); - - string line; - - if(ifs) - { - getline(ifs, line); - } - - return line; -} - -string -format(const char* format, ...) -{ - va_list ap; - - size_t buffer_size_bytes = 256; - - string result; - - char* buffer = (char*)malloc(buffer_size_bytes); - - if( buffer == NULL ) - return result; - - bool done = false; - - unsigned int loop_count = 0; - - while(!done) - { - va_start(ap, format); - - int return_value = vsnprintf(buffer, buffer_size_bytes, format, ap); - - if( return_value < 0 ) - { - done = true; - } - else if( return_value >= buffer_size_bytes ) - { - // Realloc and try again. - - buffer_size_bytes = return_value + 1; - - char* new_buffer_ptr = (char*)realloc(buffer, buffer_size_bytes); - - if( new_buffer_ptr == NULL ) - { - done = true; - } - else - { - buffer = new_buffer_ptr; - } - } - else - { - result = buffer; - done = true; - } - - va_end(ap); - - if( ++loop_count > 5 ) - done = true; - } - - free(buffer); - - return result; -} - -string -usb_sysfs_hw_string(const string& sysfs_path) -{ - string serial_number = read_line( sysfs_path + "/serial" ); - - if( serial_number.length() > 0 ) - { - serial_number = format( "SNR=%s", serial_number.c_str() ); - } - - string vid = read_line( sysfs_path + "/idVendor" ); - - string pid = read_line( sysfs_path + "/idProduct" ); - - return format("USB VID:PID=%s:%s %s", vid.c_str(), pid.c_str(), serial_number.c_str() ); -} - -vector -serial::list_ports() -{ - vector results; - - vector search_globs; - search_globs.push_back("/dev/ttyACM*"); - search_globs.push_back("/dev/ttyS*"); - search_globs.push_back("/dev/ttyUSB*"); - search_globs.push_back("/dev/tty.*"); - search_globs.push_back("/dev/cu.*"); - search_globs.push_back("/dev/rfcomm*"); - - vector devices_found = glob( search_globs ); - - vector::iterator iter = devices_found.begin(); - - while( iter != devices_found.end() ) - { - string device = *iter++; - - vector sysfs_info = get_sysfs_info( device ); - - string friendly_name = sysfs_info[0]; - - string hardware_id = sysfs_info[1]; - - PortInfo device_entry; - device_entry.port = device; - device_entry.description = friendly_name; - device_entry.hardware_id = hardware_id; - - results.push_back( device_entry ); - - } - - return results; -} - -#endif // defined(__linux__) diff --git a/serial/src/impl/list_ports/list_ports_osx.cc b/serial/src/impl/list_ports/list_ports_osx.cc deleted file mode 100644 index 333c55c..0000000 --- a/serial/src/impl/list_ports/list_ports_osx.cc +++ /dev/null @@ -1,286 +0,0 @@ -#if defined(__APPLE__) - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include "serial/serial.h" - -using serial::PortInfo; -using std::string; -using std::vector; - -#define HARDWARE_ID_STRING_LENGTH 128 - -string cfstring_to_string( CFStringRef cfstring ); -string get_device_path( io_object_t& serial_port ); -string get_class_name( io_object_t& obj ); -io_registry_entry_t get_parent_iousb_device( io_object_t& serial_port ); -string get_string_property( io_object_t& device, const char* property ); -uint16_t get_int_property( io_object_t& device, const char* property ); -string rtrim(const string& str); - -string -cfstring_to_string( CFStringRef cfstring ) -{ - char cstring[MAXPATHLEN]; - string result; - - if( cfstring ) - { - Boolean success = CFStringGetCString( cfstring, - cstring, - sizeof(cstring), - kCFStringEncodingASCII ); - - if( success ) - result = cstring; - } - - return result; -} - -string -get_device_path( io_object_t& serial_port ) -{ - CFTypeRef callout_path; - string device_path; - - callout_path = IORegistryEntryCreateCFProperty( serial_port, - CFSTR(kIOCalloutDeviceKey), - kCFAllocatorDefault, - 0 ); - - if (callout_path) - { - if( CFGetTypeID(callout_path) == CFStringGetTypeID() ) - device_path = cfstring_to_string( static_cast(callout_path) ); - - CFRelease(callout_path); - } - - return device_path; -} - -string -get_class_name( io_object_t& obj ) -{ - string result; - io_name_t class_name; - kern_return_t kern_result; - - kern_result = IOObjectGetClass( obj, class_name ); - - if( kern_result == KERN_SUCCESS ) - result = class_name; - - return result; -} - -io_registry_entry_t -get_parent_iousb_device( io_object_t& serial_port ) -{ - io_object_t device = serial_port; - io_registry_entry_t parent = 0; - io_registry_entry_t result = 0; - kern_return_t kern_result = KERN_FAILURE; - string name = get_class_name(device); - - // Walk the IO Registry tree looking for this devices parent IOUSBDevice. - while( name != "IOUSBDevice" ) - { - kern_result = IORegistryEntryGetParentEntry( device, - kIOServicePlane, - &parent ); - - if(kern_result != KERN_SUCCESS) - { - result = 0; - break; - } - - device = parent; - - name = get_class_name(device); - } - - if(kern_result == KERN_SUCCESS) - result = device; - - return result; -} - -string -get_string_property( io_object_t& device, const char* property ) -{ - string property_name; - - if( device ) - { - CFStringRef property_as_cfstring = CFStringCreateWithCString ( - kCFAllocatorDefault, - property, - kCFStringEncodingASCII ); - - CFTypeRef name_as_cfstring = IORegistryEntryCreateCFProperty( - device, - property_as_cfstring, - kCFAllocatorDefault, - 0 ); - - if( name_as_cfstring ) - { - if( CFGetTypeID(name_as_cfstring) == CFStringGetTypeID() ) - property_name = cfstring_to_string( static_cast(name_as_cfstring) ); - - CFRelease(name_as_cfstring); - } - - if(property_as_cfstring) - CFRelease(property_as_cfstring); - } - - return property_name; -} - -uint16_t -get_int_property( io_object_t& device, const char* property ) -{ - uint16_t result = 0; - - if( device ) - { - CFStringRef property_as_cfstring = CFStringCreateWithCString ( - kCFAllocatorDefault, - property, - kCFStringEncodingASCII ); - - CFTypeRef number = IORegistryEntryCreateCFProperty( device, - property_as_cfstring, - kCFAllocatorDefault, - 0 ); - - if(property_as_cfstring) - CFRelease(property_as_cfstring); - - if( number ) - { - if( CFGetTypeID(number) == CFNumberGetTypeID() ) - { - bool success = CFNumberGetValue( static_cast(number), - kCFNumberSInt16Type, - &result ); - - if( !success ) - result = 0; - } - - CFRelease(number); - } - - } - - return result; -} - -string rtrim(const string& str) -{ - string result = str; - - string whitespace = " \t\f\v\n\r"; - - std::size_t found = result.find_last_not_of(whitespace); - - if (found != std::string::npos) - result.erase(found+1); - else - result.clear(); - - return result; -} - -vector -serial::list_ports(void) -{ - vector devices_found; - CFMutableDictionaryRef classes_to_match; - io_iterator_t serial_port_iterator; - io_object_t serial_port; - mach_port_t master_port; - kern_return_t kern_result; - - kern_result = IOMasterPort(MACH_PORT_NULL, &master_port); - - if(kern_result != KERN_SUCCESS) - return devices_found; - - classes_to_match = IOServiceMatching(kIOSerialBSDServiceValue); - - if (classes_to_match == NULL) - return devices_found; - - CFDictionarySetValue( classes_to_match, - CFSTR(kIOSerialBSDTypeKey), - CFSTR(kIOSerialBSDAllTypes) ); - - kern_result = IOServiceGetMatchingServices(master_port, classes_to_match, &serial_port_iterator); - - if (KERN_SUCCESS != kern_result) - return devices_found; - - while ( (serial_port = IOIteratorNext(serial_port_iterator)) ) - { - string device_path = get_device_path( serial_port ); - io_registry_entry_t parent = get_parent_iousb_device( serial_port ); - IOObjectRelease(serial_port); - - if( device_path.empty() ) - continue; - - PortInfo port_info; - port_info.port = device_path; - port_info.description = "n/a"; - port_info.hardware_id = "n/a"; - - string device_name = rtrim( get_string_property( parent, "USB Product Name" ) ); - string vendor_name = rtrim( get_string_property( parent, "USB Vendor Name") ); - string description = rtrim( vendor_name + " " + device_name ); - if( !description.empty() ) - port_info.description = description; - - string serial_number = rtrim(get_string_property( parent, "USB Serial Number" ) ); - uint16_t vendor_id = get_int_property( parent, "idVendor" ); - uint16_t product_id = get_int_property( parent, "idProduct" ); - - if( vendor_id && product_id ) - { - char cstring[HARDWARE_ID_STRING_LENGTH]; - - if(serial_number.empty()) - serial_number = "None"; - - int ret = snprintf( cstring, HARDWARE_ID_STRING_LENGTH, "USB VID:PID=%04x:%04x SNR=%s", - vendor_id, - product_id, - serial_number.c_str() ); - - if( (ret >= 0) && (ret < HARDWARE_ID_STRING_LENGTH) ) - port_info.hardware_id = cstring; - } - - devices_found.push_back(port_info); - } - - IOObjectRelease(serial_port_iterator); - return devices_found; -} - -#endif // defined(__APPLE__) diff --git a/serial/src/impl/list_ports/list_ports_win.cc b/serial/src/impl/list_ports/list_ports_win.cc deleted file mode 100644 index 7da40c4..0000000 --- a/serial/src/impl/list_ports/list_ports_win.cc +++ /dev/null @@ -1,152 +0,0 @@ -#if defined(_WIN32) - -/* - * Copyright (c) 2014 Craig Lilley - * This software is made available under the terms of the MIT licence. - * A copy of the licence can be obtained from: - * http://opensource.org/licenses/MIT - */ - -#include "serial/serial.h" -#include -#include -#include -#include -#include -#include - -using serial::PortInfo; -using std::vector; -using std::string; - -static const DWORD port_name_max_length = 256; -static const DWORD friendly_name_max_length = 256; -static const DWORD hardware_id_max_length = 256; - -// Convert a wide Unicode string to an UTF8 string -std::string utf8_encode(const std::wstring &wstr) -{ - int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL); - std::string strTo( size_needed, 0 ); - WideCharToMultiByte (CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL); - return strTo; -} - -vector -serial::list_ports() -{ - vector devices_found; - - HDEVINFO device_info_set = SetupDiGetClassDevs( - (const GUID *) &GUID_DEVCLASS_PORTS, - NULL, - NULL, - DIGCF_PRESENT); - - unsigned int device_info_set_index = 0; - SP_DEVINFO_DATA device_info_data; - - device_info_data.cbSize = sizeof(SP_DEVINFO_DATA); - - while(SetupDiEnumDeviceInfo(device_info_set, device_info_set_index, &device_info_data)) - { - device_info_set_index++; - - // Get port name - - HKEY hkey = SetupDiOpenDevRegKey( - device_info_set, - &device_info_data, - DICS_FLAG_GLOBAL, - 0, - DIREG_DEV, - KEY_READ); - - TCHAR port_name[port_name_max_length]; - DWORD port_name_length = port_name_max_length; - - LONG return_code = RegQueryValueEx( - hkey, - _T("PortName"), - NULL, - NULL, - (LPBYTE)port_name, - &port_name_length); - - RegCloseKey(hkey); - - if(return_code != EXIT_SUCCESS) - continue; - - if(port_name_length > 0 && port_name_length <= port_name_max_length) - port_name[port_name_length-1] = '\0'; - else - port_name[0] = '\0'; - - // Ignore parallel ports - - if(_tcsstr(port_name, _T("LPT")) != NULL) - continue; - - // Get port friendly name - - TCHAR friendly_name[friendly_name_max_length]; - DWORD friendly_name_actual_length = 0; - - BOOL got_friendly_name = SetupDiGetDeviceRegistryProperty( - device_info_set, - &device_info_data, - SPDRP_FRIENDLYNAME, - NULL, - (PBYTE)friendly_name, - friendly_name_max_length, - &friendly_name_actual_length); - - if(got_friendly_name == TRUE && friendly_name_actual_length > 0) - friendly_name[friendly_name_actual_length-1] = '\0'; - else - friendly_name[0] = '\0'; - - // Get hardware ID - - TCHAR hardware_id[hardware_id_max_length]; - DWORD hardware_id_actual_length = 0; - - BOOL got_hardware_id = SetupDiGetDeviceRegistryProperty( - device_info_set, - &device_info_data, - SPDRP_HARDWAREID, - NULL, - (PBYTE)hardware_id, - hardware_id_max_length, - &hardware_id_actual_length); - - if(got_hardware_id == TRUE && hardware_id_actual_length > 0) - hardware_id[hardware_id_actual_length-1] = '\0'; - else - hardware_id[0] = '\0'; - - #ifdef UNICODE - std::string portName = utf8_encode(port_name); - std::string friendlyName = utf8_encode(friendly_name); - std::string hardwareId = utf8_encode(hardware_id); - #else - std::string portName = port_name; - std::string friendlyName = friendly_name; - std::string hardwareId = hardware_id; - #endif - - PortInfo port_entry; - port_entry.port = portName; - port_entry.description = friendlyName; - port_entry.hardware_id = hardwareId; - - devices_found.push_back(port_entry); - } - - SetupDiDestroyDeviceInfoList(device_info_set); - - return devices_found; -} - -#endif // #if defined(_WIN32) diff --git a/serial/src/impl/unix.cc b/serial/src/impl/unix.cc deleted file mode 100644 index a40b0fa..0000000 --- a/serial/src/impl/unix.cc +++ /dev/null @@ -1,1084 +0,0 @@ -/* Copyright 2012 William Woodall and John Harrison - * - * Additional Contributors: Christopher Baker @bakercp - */ - -#if !defined(_WIN32) - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(__linux__) -# include -#endif - -#include -#include -#include -#ifdef __MACH__ -#include -#include -#include -#endif - -#include "serial/impl/unix.h" - -#ifndef TIOCINQ -#ifdef FIONREAD -#define TIOCINQ FIONREAD -#else -#define TIOCINQ 0x541B -#endif -#endif - -#if defined(MAC_OS_X_VERSION_10_3) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3) -#include -#endif - -using std::string; -using std::stringstream; -using std::invalid_argument; -using serial::MillisecondTimer; -using serial::Serial; -using serial::SerialException; -using serial::PortNotOpenedException; -using serial::IOException; - - -MillisecondTimer::MillisecondTimer (const uint32_t millis) - : expiry(timespec_now()) -{ - int64_t tv_nsec = expiry.tv_nsec + (millis * 1e6); - if (tv_nsec >= 1e9) { - int64_t sec_diff = tv_nsec / static_cast (1e9); - expiry.tv_nsec = tv_nsec % static_cast(1e9); - expiry.tv_sec += sec_diff; - } else { - expiry.tv_nsec = tv_nsec; - } -} - -int64_t -MillisecondTimer::remaining () -{ - timespec now(timespec_now()); - int64_t millis = (expiry.tv_sec - now.tv_sec) * 1e3; - millis += (expiry.tv_nsec - now.tv_nsec) / 1e6; - return millis; -} - -timespec -MillisecondTimer::timespec_now () -{ - timespec time; -# ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time - clock_serv_t cclock; - mach_timespec_t mts; - host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock); - clock_get_time(cclock, &mts); - mach_port_deallocate(mach_task_self(), cclock); - time.tv_sec = mts.tv_sec; - time.tv_nsec = mts.tv_nsec; -# else - clock_gettime(CLOCK_MONOTONIC, &time); -# endif - return time; -} - -timespec -timespec_from_ms (const uint32_t millis) -{ - timespec time; - time.tv_sec = millis / 1e3; - time.tv_nsec = (millis - (time.tv_sec * 1e3)) * 1e6; - return time; -} - -Serial::SerialImpl::SerialImpl (const string &port, unsigned long baudrate, - bytesize_t bytesize, - parity_t parity, stopbits_t stopbits, - flowcontrol_t flowcontrol) - : port_ (port), fd_ (-1), is_open_ (false), xonxoff_ (false), rtscts_ (false), - baudrate_ (baudrate), parity_ (parity), - bytesize_ (bytesize), stopbits_ (stopbits), flowcontrol_ (flowcontrol) -{ - pthread_mutex_init(&this->read_mutex, NULL); - pthread_mutex_init(&this->write_mutex, NULL); - if (port_.empty () == false) - open (); -} - -Serial::SerialImpl::~SerialImpl () -{ - close(); - pthread_mutex_destroy(&this->read_mutex); - pthread_mutex_destroy(&this->write_mutex); -} - -void -Serial::SerialImpl::open () -{ - if (port_.empty ()) { - throw invalid_argument ("Empty port is invalid."); - } - if (is_open_ == true) { - throw SerialException ("Serial port already open."); - } - - fd_ = ::open (port_.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); - - if (fd_ == -1) { - switch (errno) { - case EINTR: - // Recurse because this is a recoverable error. - open (); - return; - case ENFILE: - case EMFILE: - THROW (IOException, "Too many file handles open."); - default: - THROW (IOException, errno); - } - } - - reconfigurePort(); - is_open_ = true; -} - -void -Serial::SerialImpl::reconfigurePort () -{ - if (fd_ == -1) { - // Can only operate on a valid file descriptor - THROW (IOException, "Invalid file descriptor, is the serial port open?"); - } - - struct termios options; // The options for the file descriptor - - if (tcgetattr(fd_, &options) == -1) { - THROW (IOException, "::tcgetattr"); - } - - // set up raw mode / no echo / binary - options.c_cflag |= (tcflag_t) (CLOCAL | CREAD); - options.c_lflag &= (tcflag_t) ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | - ISIG | IEXTEN); //|ECHOPRT - - options.c_oflag &= (tcflag_t) ~(OPOST); - options.c_iflag &= (tcflag_t) ~(INLCR | IGNCR | ICRNL | IGNBRK); -#ifdef IUCLC - options.c_iflag &= (tcflag_t) ~IUCLC; -#endif -#ifdef PARMRK - options.c_iflag &= (tcflag_t) ~PARMRK; -#endif - - // setup baud rate - bool custom_baud = false; - speed_t baud; - switch (baudrate_) { -#ifdef B0 - case 0: baud = B0; break; -#endif -#ifdef B50 - case 50: baud = B50; break; -#endif -#ifdef B75 - case 75: baud = B75; break; -#endif -#ifdef B110 - case 110: baud = B110; break; -#endif -#ifdef B134 - case 134: baud = B134; break; -#endif -#ifdef B150 - case 150: baud = B150; break; -#endif -#ifdef B200 - case 200: baud = B200; break; -#endif -#ifdef B300 - case 300: baud = B300; break; -#endif -#ifdef B600 - case 600: baud = B600; break; -#endif -#ifdef B1200 - case 1200: baud = B1200; break; -#endif -#ifdef B1800 - case 1800: baud = B1800; break; -#endif -#ifdef B2400 - case 2400: baud = B2400; break; -#endif -#ifdef B4800 - case 4800: baud = B4800; break; -#endif -#ifdef B7200 - case 7200: baud = B7200; break; -#endif -#ifdef B9600 - case 9600: baud = B9600; break; -#endif -#ifdef B14400 - case 14400: baud = B14400; break; -#endif -#ifdef B19200 - case 19200: baud = B19200; break; -#endif -#ifdef B28800 - case 28800: baud = B28800; break; -#endif -#ifdef B57600 - case 57600: baud = B57600; break; -#endif -#ifdef B76800 - case 76800: baud = B76800; break; -#endif -#ifdef B38400 - case 38400: baud = B38400; break; -#endif -#ifdef B115200 - case 115200: baud = B115200; break; -#endif -#ifdef B128000 - case 128000: baud = B128000; break; -#endif -#ifdef B153600 - case 153600: baud = B153600; break; -#endif -#ifdef B230400 - case 230400: baud = B230400; break; -#endif -#ifdef B256000 - case 256000: baud = B256000; break; -#endif -#ifdef B460800 - case 460800: baud = B460800; break; -#endif -#ifdef B500000 - case 500000: baud = B500000; break; -#endif -#ifdef B576000 - case 576000: baud = B576000; break; -#endif -#ifdef B921600 - case 921600: baud = B921600; break; -#endif -#ifdef B1000000 - case 1000000: baud = B1000000; break; -#endif -#ifdef B1152000 - case 1152000: baud = B1152000; break; -#endif -#ifdef B1500000 - case 1500000: baud = B1500000; break; -#endif -#ifdef B2000000 - case 2000000: baud = B2000000; break; -#endif -#ifdef B2500000 - case 2500000: baud = B2500000; break; -#endif -#ifdef B3000000 - case 3000000: baud = B3000000; break; -#endif -#ifdef B3500000 - case 3500000: baud = B3500000; break; -#endif -#ifdef B4000000 - case 4000000: baud = B4000000; break; -#endif - default: - custom_baud = true; - } - if (custom_baud == false) { -#ifdef _BSD_SOURCE - ::cfsetspeed(&options, baud); -#else - ::cfsetispeed(&options, baud); - ::cfsetospeed(&options, baud); -#endif - } - - // setup char len - options.c_cflag &= (tcflag_t) ~CSIZE; - if (bytesize_ == eightbits) - options.c_cflag |= CS8; - else if (bytesize_ == sevenbits) - options.c_cflag |= CS7; - else if (bytesize_ == sixbits) - options.c_cflag |= CS6; - else if (bytesize_ == fivebits) - options.c_cflag |= CS5; - else - throw invalid_argument ("invalid char len"); - // setup stopbits - if (stopbits_ == stopbits_one) - options.c_cflag &= (tcflag_t) ~(CSTOPB); - else if (stopbits_ == stopbits_one_point_five) - // ONE POINT FIVE same as TWO.. there is no POSIX support for 1.5 - options.c_cflag |= (CSTOPB); - else if (stopbits_ == stopbits_two) - options.c_cflag |= (CSTOPB); - else - throw invalid_argument ("invalid stop bit"); - // setup parity - options.c_iflag &= (tcflag_t) ~(INPCK | ISTRIP); - if (parity_ == parity_none) { - options.c_cflag &= (tcflag_t) ~(PARENB | PARODD); - } else if (parity_ == parity_even) { - options.c_cflag &= (tcflag_t) ~(PARODD); - options.c_cflag |= (PARENB); - } else if (parity_ == parity_odd) { - options.c_cflag |= (PARENB | PARODD); - } -#ifdef CMSPAR - else if (parity_ == parity_mark) { - options.c_cflag |= (PARENB | CMSPAR | PARODD); - } - else if (parity_ == parity_space) { - options.c_cflag |= (PARENB | CMSPAR); - options.c_cflag &= (tcflag_t) ~(PARODD); - } -#else - // CMSPAR is not defined on OSX. So do not support mark or space parity. - else if (parity_ == parity_mark || parity_ == parity_space) { - throw invalid_argument ("OS does not support mark or space parity"); - } -#endif // ifdef CMSPAR - else { - throw invalid_argument ("invalid parity"); - } - // setup flow control - if (flowcontrol_ == flowcontrol_none) { - xonxoff_ = false; - rtscts_ = false; - } - if (flowcontrol_ == flowcontrol_software) { - xonxoff_ = true; - rtscts_ = false; - } - if (flowcontrol_ == flowcontrol_hardware) { - xonxoff_ = false; - rtscts_ = true; - } - // xonxoff -#ifdef IXANY - if (xonxoff_) - options.c_iflag |= (IXON | IXOFF); //|IXANY) - else - options.c_iflag &= (tcflag_t) ~(IXON | IXOFF | IXANY); -#else - if (xonxoff_) - options.c_iflag |= (IXON | IXOFF); - else - options.c_iflag &= (tcflag_t) ~(IXON | IXOFF); -#endif - // rtscts -#ifdef CRTSCTS - if (rtscts_) - options.c_cflag |= (CRTSCTS); - else - options.c_cflag &= (unsigned long) ~(CRTSCTS); -#elif defined CNEW_RTSCTS - if (rtscts_) - options.c_cflag |= (CNEW_RTSCTS); - else - options.c_cflag &= (unsigned long) ~(CNEW_RTSCTS); -#else -#error "OS Support seems wrong." -#endif - - // http://www.unixwiz.net/techtips/termios-vmin-vtime.html - // this basically sets the read call up to be a polling read, - // but we are using select to ensure there is data available - // to read before each call, so we should never needlessly poll - options.c_cc[VMIN] = 0; - options.c_cc[VTIME] = 0; - - // activate settings - ::tcsetattr (fd_, TCSANOW, &options); - - // apply custom baud rate, if any - if (custom_baud == true) { - // OS X support -#if defined(MAC_OS_X_VERSION_10_4) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4) - // Starting with Tiger, the IOSSIOSPEED ioctl can be used to set arbitrary baud rates - // other than those specified by POSIX. The driver for the underlying serial hardware - // ultimately determines which baud rates can be used. This ioctl sets both the input - // and output speed. - speed_t new_baud = static_cast (baudrate_); - // PySerial uses IOSSIOSPEED=0x80045402 - if (-1 == ioctl (fd_, IOSSIOSPEED, &new_baud, 1)) { - THROW (IOException, errno); - } - // Linux Support -#elif defined(__linux__) && defined (TIOCSSERIAL) - struct serial_struct ser; - - if (-1 == ioctl (fd_, TIOCGSERIAL, &ser)) { - THROW (IOException, errno); - } - - // set custom divisor - ser.custom_divisor = ser.baud_base / static_cast (baudrate_); - // update flags - ser.flags &= ~ASYNC_SPD_MASK; - ser.flags |= ASYNC_SPD_CUST; - - if (-1 == ioctl (fd_, TIOCSSERIAL, &ser)) { - THROW (IOException, errno); - } -#else - throw invalid_argument ("OS does not currently support custom bauds"); -#endif - } - - // Update byte_time_ based on the new settings. - uint32_t bit_time_ns = 1e9 / baudrate_; - byte_time_ns_ = bit_time_ns * (1 + bytesize_ + parity_ + stopbits_); - - // Compensate for the stopbits_one_point_five enum being equal to int 3, - // and not 1.5. - if (stopbits_ == stopbits_one_point_five) { - byte_time_ns_ += ((1.5 - stopbits_one_point_five) * bit_time_ns); - } -} - -void -Serial::SerialImpl::close () -{ - if (is_open_ == true) { - if (fd_ != -1) { - int ret; - ret = ::close (fd_); - if (ret == 0) { - fd_ = -1; - } else { - THROW (IOException, errno); - } - } - is_open_ = false; - } -} - -bool -Serial::SerialImpl::isOpen () const -{ - return is_open_; -} - -size_t -Serial::SerialImpl::available () -{ - if (!is_open_) { - return 0; - } - int count = 0; - if (-1 == ioctl (fd_, TIOCINQ, &count)) { - THROW (IOException, errno); - } else { - return static_cast (count); - } -} - -bool -Serial::SerialImpl::waitReadable (uint32_t timeout) -{ - // Setup a select call to block for serial data or a timeout - fd_set readfds; - FD_ZERO (&readfds); - FD_SET (fd_, &readfds); - timespec timeout_ts (timespec_from_ms (timeout)); - int r = pselect (fd_ + 1, &readfds, NULL, NULL, &timeout_ts, NULL); - - if (r < 0) { - // Select was interrupted - if (errno == EINTR) { - return false; - } - // Otherwise there was some error - THROW (IOException, errno); - } - // Timeout occurred - if (r == 0) { - return false; - } - // This shouldn't happen, if r > 0 our fd has to be in the list! - if (!FD_ISSET (fd_, &readfds)) { - THROW (IOException, "select reports ready to read, but our fd isn't" - " in the list, this shouldn't happen!"); - } - // Data available to read. - return true; -} - -void -Serial::SerialImpl::waitByteTimes (size_t count) -{ - timespec wait_time = { 0, static_cast(byte_time_ns_ * count)}; - pselect (0, NULL, NULL, NULL, &wait_time, NULL); -} - -size_t -Serial::SerialImpl::read (uint8_t *buf, size_t size) -{ - // If the port is not open, throw - if (!is_open_) { - throw PortNotOpenedException ("Serial::read"); - } - size_t bytes_read = 0; - - // Calculate total timeout in milliseconds t_c + (t_m * N) - long total_timeout_ms = timeout_.read_timeout_constant; - total_timeout_ms += timeout_.read_timeout_multiplier * static_cast (size); - MillisecondTimer total_timeout(total_timeout_ms); - - // Pre-fill buffer with available bytes - { - ssize_t bytes_read_now = ::read (fd_, buf, size); - if (bytes_read_now > 0) { - bytes_read = bytes_read_now; - } - } - - while (bytes_read < size) { - int64_t timeout_remaining_ms = total_timeout.remaining(); - if (timeout_remaining_ms <= 0) { - // Timed out - break; - } - // Timeout for the next select is whichever is less of the remaining - // total read timeout and the inter-byte timeout. - uint32_t timeout = std::min(static_cast (timeout_remaining_ms), - timeout_.inter_byte_timeout); - // Wait for the device to be readable, and then attempt to read. - if (waitReadable(timeout)) { - // If it's a fixed-length multi-byte read, insert a wait here so that - // we can attempt to grab the whole thing in a single IO call. Skip - // this wait if a non-max inter_byte_timeout is specified. - if (size > 1 && timeout_.inter_byte_timeout == Timeout::max()) { - size_t bytes_available = available(); - if (bytes_available + bytes_read < size) { - waitByteTimes(size - (bytes_available + bytes_read)); - } - } - // This should be non-blocking returning only what is available now - // Then returning so that select can block again. - ssize_t bytes_read_now = - ::read (fd_, buf + bytes_read, size - bytes_read); - // read should always return some data as select reported it was - // ready to read when we get to this point. - if (bytes_read_now < 1) { - // Disconnected devices, at least on Linux, show the - // behavior that they are always ready to read immediately - // but reading returns nothing. - throw SerialException ("device reports readiness to read but " - "returned no data (device disconnected?)"); - } - // Update bytes_read - bytes_read += static_cast (bytes_read_now); - // If bytes_read == size then we have read everything we need - if (bytes_read == size) { - break; - } - // If bytes_read < size then we have more to read - if (bytes_read < size) { - continue; - } - // If bytes_read > size then we have over read, which shouldn't happen - if (bytes_read > size) { - throw SerialException ("read over read, too many bytes where " - "read, this shouldn't happen, might be " - "a logical error!"); - } - } - } - return bytes_read; -} - -size_t -Serial::SerialImpl::write (const uint8_t *data, size_t length) -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::write"); - } - fd_set writefds; - size_t bytes_written = 0; - - // Calculate total timeout in milliseconds t_c + (t_m * N) - long total_timeout_ms = timeout_.write_timeout_constant; - total_timeout_ms += timeout_.write_timeout_multiplier * static_cast (length); - MillisecondTimer total_timeout(total_timeout_ms); - - bool first_iteration = true; - while (bytes_written < length) { - int64_t timeout_remaining_ms = total_timeout.remaining(); - // Only consider the timeout if it's not the first iteration of the loop - // otherwise a timeout of 0 won't be allowed through - if (!first_iteration && (timeout_remaining_ms <= 0)) { - // Timed out - break; - } - first_iteration = false; - - timespec timeout(timespec_from_ms(timeout_remaining_ms)); - - FD_ZERO (&writefds); - FD_SET (fd_, &writefds); - - // Do the select - int r = pselect (fd_ + 1, NULL, &writefds, NULL, &timeout, NULL); - - // Figure out what happened by looking at select's response 'r' - /** Error **/ - if (r < 0) { - // Select was interrupted, try again - if (errno == EINTR) { - continue; - } - // Otherwise there was some error - THROW (IOException, errno); - } - /** Timeout **/ - if (r == 0) { - break; - } - /** Port ready to write **/ - if (r > 0) { - // Make sure our file descriptor is in the ready to write list - if (FD_ISSET (fd_, &writefds)) { - // This will write some - ssize_t bytes_written_now = - ::write (fd_, data + bytes_written, length - bytes_written); - - // even though pselect returned readiness the call might still be - // interrupted. In that case simply retry. - if (bytes_written_now == -1 && errno == EINTR) { - continue; - } - - // write should always return some data as select reported it was - // ready to write when we get to this point. - if (bytes_written_now < 1) { - // Disconnected devices, at least on Linux, show the - // behavior that they are always ready to write immediately - // but writing returns nothing. - std::stringstream strs; - strs << "device reports readiness to write but " - "returned no data (device disconnected?)"; - strs << " errno=" << errno; - strs << " bytes_written_now= " << bytes_written_now; - strs << " bytes_written=" << bytes_written; - strs << " length=" << length; - throw SerialException(strs.str().c_str()); - } - // Update bytes_written - bytes_written += static_cast (bytes_written_now); - // If bytes_written == size then we have written everything we need to - if (bytes_written == length) { - break; - } - // If bytes_written < size then we have more to write - if (bytes_written < length) { - continue; - } - // If bytes_written > size then we have over written, which shouldn't happen - if (bytes_written > length) { - throw SerialException ("write over wrote, too many bytes where " - "written, this shouldn't happen, might be " - "a logical error!"); - } - } - // This shouldn't happen, if r > 0 our fd has to be in the list! - THROW (IOException, "select reports ready to write, but our fd isn't" - " in the list, this shouldn't happen!"); - } - } - return bytes_written; -} - -void -Serial::SerialImpl::setPort (const string &port) -{ - port_ = port; -} - -string -Serial::SerialImpl::getPort () const -{ - return port_; -} - -void -Serial::SerialImpl::setTimeout (serial::Timeout &timeout) -{ - timeout_ = timeout; -} - -serial::Timeout -Serial::SerialImpl::getTimeout () const -{ - return timeout_; -} - -void -Serial::SerialImpl::setBaudrate (unsigned long baudrate) -{ - baudrate_ = baudrate; - if (is_open_) - reconfigurePort (); -} - -unsigned long -Serial::SerialImpl::getBaudrate () const -{ - return baudrate_; -} - -void -Serial::SerialImpl::setBytesize (serial::bytesize_t bytesize) -{ - bytesize_ = bytesize; - if (is_open_) - reconfigurePort (); -} - -serial::bytesize_t -Serial::SerialImpl::getBytesize () const -{ - return bytesize_; -} - -void -Serial::SerialImpl::setParity (serial::parity_t parity) -{ - parity_ = parity; - if (is_open_) - reconfigurePort (); -} - -serial::parity_t -Serial::SerialImpl::getParity () const -{ - return parity_; -} - -void -Serial::SerialImpl::setStopbits (serial::stopbits_t stopbits) -{ - stopbits_ = stopbits; - if (is_open_) - reconfigurePort (); -} - -serial::stopbits_t -Serial::SerialImpl::getStopbits () const -{ - return stopbits_; -} - -void -Serial::SerialImpl::setFlowcontrol (serial::flowcontrol_t flowcontrol) -{ - flowcontrol_ = flowcontrol; - if (is_open_) - reconfigurePort (); -} - -serial::flowcontrol_t -Serial::SerialImpl::getFlowcontrol () const -{ - return flowcontrol_; -} - -void -Serial::SerialImpl::flush () -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::flush"); - } - tcdrain (fd_); -} - -void -Serial::SerialImpl::flushInput () -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::flushInput"); - } - tcflush (fd_, TCIFLUSH); -} - -void -Serial::SerialImpl::flushOutput () -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::flushOutput"); - } - tcflush (fd_, TCOFLUSH); -} - -void -Serial::SerialImpl::sendBreak (int duration) -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::sendBreak"); - } - tcsendbreak (fd_, static_cast (duration / 4)); -} - -void -Serial::SerialImpl::setBreak (bool level) -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::setBreak"); - } - - if (level) { - if (-1 == ioctl (fd_, TIOCSBRK)) - { - stringstream ss; - ss << "setBreak failed on a call to ioctl(TIOCSBRK): " << errno << " " << strerror(errno); - throw(SerialException(ss.str().c_str())); - } - } else { - if (-1 == ioctl (fd_, TIOCCBRK)) - { - stringstream ss; - ss << "setBreak failed on a call to ioctl(TIOCCBRK): " << errno << " " << strerror(errno); - throw(SerialException(ss.str().c_str())); - } - } -} - -void -Serial::SerialImpl::setRTS (bool level) -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::setRTS"); - } - - int command = TIOCM_RTS; - - if (level) { - if (-1 == ioctl (fd_, TIOCMBIS, &command)) - { - stringstream ss; - ss << "setRTS failed on a call to ioctl(TIOCMBIS): " << errno << " " << strerror(errno); - throw(SerialException(ss.str().c_str())); - } - } else { - if (-1 == ioctl (fd_, TIOCMBIC, &command)) - { - stringstream ss; - ss << "setRTS failed on a call to ioctl(TIOCMBIC): " << errno << " " << strerror(errno); - throw(SerialException(ss.str().c_str())); - } - } -} - -void -Serial::SerialImpl::setDTR (bool level) -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::setDTR"); - } - - int command = TIOCM_DTR; - - if (level) { - if (-1 == ioctl (fd_, TIOCMBIS, &command)) - { - stringstream ss; - ss << "setDTR failed on a call to ioctl(TIOCMBIS): " << errno << " " << strerror(errno); - throw(SerialException(ss.str().c_str())); - } - } else { - if (-1 == ioctl (fd_, TIOCMBIC, &command)) - { - stringstream ss; - ss << "setDTR failed on a call to ioctl(TIOCMBIC): " << errno << " " << strerror(errno); - throw(SerialException(ss.str().c_str())); - } - } -} - -bool -Serial::SerialImpl::waitForChange () -{ -#ifndef TIOCMIWAIT - -while (is_open_ == true) { - - int status; - - if (-1 == ioctl (fd_, TIOCMGET, &status)) - { - stringstream ss; - ss << "waitForChange failed on a call to ioctl(TIOCMGET): " << errno << " " << strerror(errno); - throw(SerialException(ss.str().c_str())); - } - else - { - if (0 != (status & TIOCM_CTS) - || 0 != (status & TIOCM_DSR) - || 0 != (status & TIOCM_RI) - || 0 != (status & TIOCM_CD)) - { - return true; - } - } - - usleep(1000); - } - - return false; -#else - int command = (TIOCM_CD|TIOCM_DSR|TIOCM_RI|TIOCM_CTS); - - if (-1 == ioctl (fd_, TIOCMIWAIT, &command)) { - stringstream ss; - ss << "waitForDSR failed on a call to ioctl(TIOCMIWAIT): " - << errno << " " << strerror(errno); - throw(SerialException(ss.str().c_str())); - } - return true; -#endif -} - -bool -Serial::SerialImpl::getCTS () -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::getCTS"); - } - - int status; - - if (-1 == ioctl (fd_, TIOCMGET, &status)) - { - stringstream ss; - ss << "getCTS failed on a call to ioctl(TIOCMGET): " << errno << " " << strerror(errno); - throw(SerialException(ss.str().c_str())); - } - else - { - return 0 != (status & TIOCM_CTS); - } -} - -bool -Serial::SerialImpl::getDSR () -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::getDSR"); - } - - int status; - - if (-1 == ioctl (fd_, TIOCMGET, &status)) - { - stringstream ss; - ss << "getDSR failed on a call to ioctl(TIOCMGET): " << errno << " " << strerror(errno); - throw(SerialException(ss.str().c_str())); - } - else - { - return 0 != (status & TIOCM_DSR); - } -} - -bool -Serial::SerialImpl::getRI () -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::getRI"); - } - - int status; - - if (-1 == ioctl (fd_, TIOCMGET, &status)) - { - stringstream ss; - ss << "getRI failed on a call to ioctl(TIOCMGET): " << errno << " " << strerror(errno); - throw(SerialException(ss.str().c_str())); - } - else - { - return 0 != (status & TIOCM_RI); - } -} - -bool -Serial::SerialImpl::getCD () -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::getCD"); - } - - int status; - - if (-1 == ioctl (fd_, TIOCMGET, &status)) - { - stringstream ss; - ss << "getCD failed on a call to ioctl(TIOCMGET): " << errno << " " << strerror(errno); - throw(SerialException(ss.str().c_str())); - } - else - { - return 0 != (status & TIOCM_CD); - } -} - -void -Serial::SerialImpl::readLock () -{ - int result = pthread_mutex_lock(&this->read_mutex); - if (result) { - THROW (IOException, result); - } -} - -void -Serial::SerialImpl::readUnlock () -{ - int result = pthread_mutex_unlock(&this->read_mutex); - if (result) { - THROW (IOException, result); - } -} - -void -Serial::SerialImpl::writeLock () -{ - int result = pthread_mutex_lock(&this->write_mutex); - if (result) { - THROW (IOException, result); - } -} - -void -Serial::SerialImpl::writeUnlock () -{ - int result = pthread_mutex_unlock(&this->write_mutex); - if (result) { - THROW (IOException, result); - } -} - -#endif // !defined(_WIN32) diff --git a/serial/src/impl/win.cc b/serial/src/impl/win.cc deleted file mode 100644 index 889e06f..0000000 --- a/serial/src/impl/win.cc +++ /dev/null @@ -1,646 +0,0 @@ -#if defined(_WIN32) - -/* Copyright 2012 William Woodall and John Harrison */ - -#include - -#include "serial/impl/win.h" - -using std::string; -using std::wstring; -using std::stringstream; -using std::invalid_argument; -using serial::Serial; -using serial::Timeout; -using serial::bytesize_t; -using serial::parity_t; -using serial::stopbits_t; -using serial::flowcontrol_t; -using serial::SerialException; -using serial::PortNotOpenedException; -using serial::IOException; - -inline wstring -_prefix_port_if_needed(const wstring &input) -{ - static wstring windows_com_port_prefix = L"\\\\.\\"; - if (input.compare(0, windows_com_port_prefix.size(), windows_com_port_prefix) != 0) - { - return windows_com_port_prefix + input; - } - return input; -} - -Serial::SerialImpl::SerialImpl (const string &port, unsigned long baudrate, - bytesize_t bytesize, - parity_t parity, stopbits_t stopbits, - flowcontrol_t flowcontrol) - : port_ (port.begin(), port.end()), fd_ (INVALID_HANDLE_VALUE), is_open_ (false), - baudrate_ (baudrate), parity_ (parity), - bytesize_ (bytesize), stopbits_ (stopbits), flowcontrol_ (flowcontrol) -{ - if (port_.empty () == false) - open (); - read_mutex = CreateMutex(NULL, false, NULL); - write_mutex = CreateMutex(NULL, false, NULL); -} - -Serial::SerialImpl::~SerialImpl () -{ - this->close(); - CloseHandle(read_mutex); - CloseHandle(write_mutex); -} - -void -Serial::SerialImpl::open () -{ - if (port_.empty ()) { - throw invalid_argument ("Empty port is invalid."); - } - if (is_open_ == true) { - throw SerialException ("Serial port already open."); - } - - // See: https://github.com/wjwwood/serial/issues/84 - wstring port_with_prefix = _prefix_port_if_needed(port_); - LPCWSTR lp_port = port_with_prefix.c_str(); - fd_ = CreateFileW(lp_port, - GENERIC_READ | GENERIC_WRITE, - 0, - 0, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - 0); - - if (fd_ == INVALID_HANDLE_VALUE) { - DWORD create_file_err = GetLastError(); - stringstream ss; - switch (create_file_err) { - case ERROR_FILE_NOT_FOUND: - // Use this->getPort to convert to a std::string - ss << "Specified port, " << this->getPort() << ", does not exist."; - THROW (IOException, ss.str().c_str()); - default: - ss << "Unknown error opening the serial port: " << create_file_err; - THROW (IOException, ss.str().c_str()); - } - } - - reconfigurePort(); - is_open_ = true; -} - -void -Serial::SerialImpl::reconfigurePort () -{ - if (fd_ == INVALID_HANDLE_VALUE) { - // Can only operate on a valid file descriptor - THROW (IOException, "Invalid file descriptor, is the serial port open?"); - } - - DCB dcbSerialParams = {0}; - - dcbSerialParams.DCBlength=sizeof(dcbSerialParams); - - if (!GetCommState(fd_, &dcbSerialParams)) { - //error getting state - THROW (IOException, "Error getting the serial port state."); - } - - // setup baud rate - switch (baudrate_) { -#ifdef CBR_0 - case 0: dcbSerialParams.BaudRate = CBR_0; break; -#endif -#ifdef CBR_50 - case 50: dcbSerialParams.BaudRate = CBR_50; break; -#endif -#ifdef CBR_75 - case 75: dcbSerialParams.BaudRate = CBR_75; break; -#endif -#ifdef CBR_110 - case 110: dcbSerialParams.BaudRate = CBR_110; break; -#endif -#ifdef CBR_134 - case 134: dcbSerialParams.BaudRate = CBR_134; break; -#endif -#ifdef CBR_150 - case 150: dcbSerialParams.BaudRate = CBR_150; break; -#endif -#ifdef CBR_200 - case 200: dcbSerialParams.BaudRate = CBR_200; break; -#endif -#ifdef CBR_300 - case 300: dcbSerialParams.BaudRate = CBR_300; break; -#endif -#ifdef CBR_600 - case 600: dcbSerialParams.BaudRate = CBR_600; break; -#endif -#ifdef CBR_1200 - case 1200: dcbSerialParams.BaudRate = CBR_1200; break; -#endif -#ifdef CBR_1800 - case 1800: dcbSerialParams.BaudRate = CBR_1800; break; -#endif -#ifdef CBR_2400 - case 2400: dcbSerialParams.BaudRate = CBR_2400; break; -#endif -#ifdef CBR_4800 - case 4800: dcbSerialParams.BaudRate = CBR_4800; break; -#endif -#ifdef CBR_7200 - case 7200: dcbSerialParams.BaudRate = CBR_7200; break; -#endif -#ifdef CBR_9600 - case 9600: dcbSerialParams.BaudRate = CBR_9600; break; -#endif -#ifdef CBR_14400 - case 14400: dcbSerialParams.BaudRate = CBR_14400; break; -#endif -#ifdef CBR_19200 - case 19200: dcbSerialParams.BaudRate = CBR_19200; break; -#endif -#ifdef CBR_28800 - case 28800: dcbSerialParams.BaudRate = CBR_28800; break; -#endif -#ifdef CBR_57600 - case 57600: dcbSerialParams.BaudRate = CBR_57600; break; -#endif -#ifdef CBR_76800 - case 76800: dcbSerialParams.BaudRate = CBR_76800; break; -#endif -#ifdef CBR_38400 - case 38400: dcbSerialParams.BaudRate = CBR_38400; break; -#endif -#ifdef CBR_115200 - case 115200: dcbSerialParams.BaudRate = CBR_115200; break; -#endif -#ifdef CBR_128000 - case 128000: dcbSerialParams.BaudRate = CBR_128000; break; -#endif -#ifdef CBR_153600 - case 153600: dcbSerialParams.BaudRate = CBR_153600; break; -#endif -#ifdef CBR_230400 - case 230400: dcbSerialParams.BaudRate = CBR_230400; break; -#endif -#ifdef CBR_256000 - case 256000: dcbSerialParams.BaudRate = CBR_256000; break; -#endif -#ifdef CBR_460800 - case 460800: dcbSerialParams.BaudRate = CBR_460800; break; -#endif -#ifdef CBR_921600 - case 921600: dcbSerialParams.BaudRate = CBR_921600; break; -#endif - default: - // Try to blindly assign it - dcbSerialParams.BaudRate = baudrate_; - } - - // setup char len - if (bytesize_ == eightbits) - dcbSerialParams.ByteSize = 8; - else if (bytesize_ == sevenbits) - dcbSerialParams.ByteSize = 7; - else if (bytesize_ == sixbits) - dcbSerialParams.ByteSize = 6; - else if (bytesize_ == fivebits) - dcbSerialParams.ByteSize = 5; - else - throw invalid_argument ("invalid char len"); - - // setup stopbits - if (stopbits_ == stopbits_one) - dcbSerialParams.StopBits = ONESTOPBIT; - else if (stopbits_ == stopbits_one_point_five) - dcbSerialParams.StopBits = ONE5STOPBITS; - else if (stopbits_ == stopbits_two) - dcbSerialParams.StopBits = TWOSTOPBITS; - else - throw invalid_argument ("invalid stop bit"); - - // setup parity - if (parity_ == parity_none) { - dcbSerialParams.Parity = NOPARITY; - } else if (parity_ == parity_even) { - dcbSerialParams.Parity = EVENPARITY; - } else if (parity_ == parity_odd) { - dcbSerialParams.Parity = ODDPARITY; - } else if (parity_ == parity_mark) { - dcbSerialParams.Parity = MARKPARITY; - } else if (parity_ == parity_space) { - dcbSerialParams.Parity = SPACEPARITY; - } else { - throw invalid_argument ("invalid parity"); - } - - // setup flowcontrol - if (flowcontrol_ == flowcontrol_none) { - dcbSerialParams.fOutxCtsFlow = false; - dcbSerialParams.fRtsControl = RTS_CONTROL_DISABLE; - dcbSerialParams.fOutX = false; - dcbSerialParams.fInX = false; - } - if (flowcontrol_ == flowcontrol_software) { - dcbSerialParams.fOutxCtsFlow = false; - dcbSerialParams.fRtsControl = RTS_CONTROL_DISABLE; - dcbSerialParams.fOutX = true; - dcbSerialParams.fInX = true; - } - if (flowcontrol_ == flowcontrol_hardware) { - dcbSerialParams.fOutxCtsFlow = true; - dcbSerialParams.fRtsControl = RTS_CONTROL_HANDSHAKE; - dcbSerialParams.fOutX = false; - dcbSerialParams.fInX = false; - } - - // activate settings - if (!SetCommState(fd_, &dcbSerialParams)){ - CloseHandle(fd_); - THROW (IOException, "Error setting serial port settings."); - } - - // Setup timeouts - COMMTIMEOUTS timeouts = {0}; - timeouts.ReadIntervalTimeout = timeout_.inter_byte_timeout; - timeouts.ReadTotalTimeoutConstant = timeout_.read_timeout_constant; - timeouts.ReadTotalTimeoutMultiplier = timeout_.read_timeout_multiplier; - timeouts.WriteTotalTimeoutConstant = timeout_.write_timeout_constant; - timeouts.WriteTotalTimeoutMultiplier = timeout_.write_timeout_multiplier; - if (!SetCommTimeouts(fd_, &timeouts)) { - THROW (IOException, "Error setting timeouts."); - } -} - -void -Serial::SerialImpl::close () -{ - if (is_open_ == true) { - if (fd_ != INVALID_HANDLE_VALUE) { - int ret; - ret = CloseHandle(fd_); - if (ret == 0) { - stringstream ss; - ss << "Error while closing serial port: " << GetLastError(); - THROW (IOException, ss.str().c_str()); - } else { - fd_ = INVALID_HANDLE_VALUE; - } - } - is_open_ = false; - } -} - -bool -Serial::SerialImpl::isOpen () const -{ - return is_open_; -} - -size_t -Serial::SerialImpl::available () -{ - if (!is_open_) { - return 0; - } - COMSTAT cs; - if (!ClearCommError(fd_, NULL, &cs)) { - stringstream ss; - ss << "Error while checking status of the serial port: " << GetLastError(); - THROW (IOException, ss.str().c_str()); - } - return static_cast(cs.cbInQue); -} - -bool -Serial::SerialImpl::waitReadable (uint32_t /*timeout*/) -{ - THROW (IOException, "waitReadable is not implemented on Windows."); - return false; -} - -void -Serial::SerialImpl::waitByteTimes (size_t /*count*/) -{ - THROW (IOException, "waitByteTimes is not implemented on Windows."); -} - -size_t -Serial::SerialImpl::read (uint8_t *buf, size_t size) -{ - if (!is_open_) { - throw PortNotOpenedException ("Serial::read"); - } - DWORD bytes_read; - if (!ReadFile(fd_, buf, static_cast(size), &bytes_read, NULL)) { - stringstream ss; - ss << "Error while reading from the serial port: " << GetLastError(); - THROW (IOException, ss.str().c_str()); - } - return (size_t) (bytes_read); -} - -size_t -Serial::SerialImpl::write (const uint8_t *data, size_t length) -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::write"); - } - DWORD bytes_written; - if (!WriteFile(fd_, data, static_cast(length), &bytes_written, NULL)) { - stringstream ss; - ss << "Error while writing to the serial port: " << GetLastError(); - THROW (IOException, ss.str().c_str()); - } - return (size_t) (bytes_written); -} - -void -Serial::SerialImpl::setPort (const string &port) -{ - port_ = wstring(port.begin(), port.end()); -} - -string -Serial::SerialImpl::getPort () const -{ - return string(port_.begin(), port_.end()); -} - -void -Serial::SerialImpl::setTimeout (serial::Timeout &timeout) -{ - timeout_ = timeout; - if (is_open_) { - reconfigurePort (); - } -} - -serial::Timeout -Serial::SerialImpl::getTimeout () const -{ - return timeout_; -} - -void -Serial::SerialImpl::setBaudrate (unsigned long baudrate) -{ - baudrate_ = baudrate; - if (is_open_) { - reconfigurePort (); - } -} - -unsigned long -Serial::SerialImpl::getBaudrate () const -{ - return baudrate_; -} - -void -Serial::SerialImpl::setBytesize (serial::bytesize_t bytesize) -{ - bytesize_ = bytesize; - if (is_open_) { - reconfigurePort (); - } -} - -serial::bytesize_t -Serial::SerialImpl::getBytesize () const -{ - return bytesize_; -} - -void -Serial::SerialImpl::setParity (serial::parity_t parity) -{ - parity_ = parity; - if (is_open_) { - reconfigurePort (); - } -} - -serial::parity_t -Serial::SerialImpl::getParity () const -{ - return parity_; -} - -void -Serial::SerialImpl::setStopbits (serial::stopbits_t stopbits) -{ - stopbits_ = stopbits; - if (is_open_) { - reconfigurePort (); - } -} - -serial::stopbits_t -Serial::SerialImpl::getStopbits () const -{ - return stopbits_; -} - -void -Serial::SerialImpl::setFlowcontrol (serial::flowcontrol_t flowcontrol) -{ - flowcontrol_ = flowcontrol; - if (is_open_) { - reconfigurePort (); - } -} - -serial::flowcontrol_t -Serial::SerialImpl::getFlowcontrol () const -{ - return flowcontrol_; -} - -void -Serial::SerialImpl::flush () -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::flush"); - } - FlushFileBuffers (fd_); -} - -void -Serial::SerialImpl::flushInput () -{ - if (is_open_ == false) { - throw PortNotOpenedException("Serial::flushInput"); - } - PurgeComm(fd_, PURGE_RXCLEAR); -} - -void -Serial::SerialImpl::flushOutput () -{ - if (is_open_ == false) { - throw PortNotOpenedException("Serial::flushOutput"); - } - PurgeComm(fd_, PURGE_TXCLEAR); -} - -void -Serial::SerialImpl::sendBreak (int /*duration*/) -{ - THROW (IOException, "sendBreak is not supported on Windows."); -} - -void -Serial::SerialImpl::setBreak (bool level) -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::setBreak"); - } - if (level) { - EscapeCommFunction (fd_, SETBREAK); - } else { - EscapeCommFunction (fd_, CLRBREAK); - } -} - -void -Serial::SerialImpl::setRTS (bool level) -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::setRTS"); - } - if (level) { - EscapeCommFunction (fd_, SETRTS); - } else { - EscapeCommFunction (fd_, CLRRTS); - } -} - -void -Serial::SerialImpl::setDTR (bool level) -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::setDTR"); - } - if (level) { - EscapeCommFunction (fd_, SETDTR); - } else { - EscapeCommFunction (fd_, CLRDTR); - } -} - -bool -Serial::SerialImpl::waitForChange () -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::waitForChange"); - } - DWORD dwCommEvent; - - if (!SetCommMask(fd_, EV_CTS | EV_DSR | EV_RING | EV_RLSD)) { - // Error setting communications mask - return false; - } - - if (!WaitCommEvent(fd_, &dwCommEvent, NULL)) { - // An error occurred waiting for the event. - return false; - } else { - // Event has occurred. - return true; - } -} - -bool -Serial::SerialImpl::getCTS () -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::getCTS"); - } - DWORD dwModemStatus; - if (!GetCommModemStatus(fd_, &dwModemStatus)) { - THROW (IOException, "Error getting the status of the CTS line."); - } - - return (MS_CTS_ON & dwModemStatus) != 0; -} - -bool -Serial::SerialImpl::getDSR () -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::getDSR"); - } - DWORD dwModemStatus; - if (!GetCommModemStatus(fd_, &dwModemStatus)) { - THROW (IOException, "Error getting the status of the DSR line."); - } - - return (MS_DSR_ON & dwModemStatus) != 0; -} - -bool -Serial::SerialImpl::getRI() -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::getRI"); - } - DWORD dwModemStatus; - if (!GetCommModemStatus(fd_, &dwModemStatus)) { - THROW (IOException, "Error getting the status of the RI line."); - } - - return (MS_RING_ON & dwModemStatus) != 0; -} - -bool -Serial::SerialImpl::getCD() -{ - if (is_open_ == false) { - throw PortNotOpenedException ("Serial::getCD"); - } - DWORD dwModemStatus; - if (!GetCommModemStatus(fd_, &dwModemStatus)) { - // Error in GetCommModemStatus; - THROW (IOException, "Error getting the status of the CD line."); - } - - return (MS_RLSD_ON & dwModemStatus) != 0; -} - -void -Serial::SerialImpl::readLock() -{ - if (WaitForSingleObject(read_mutex, INFINITE) != WAIT_OBJECT_0) { - THROW (IOException, "Error claiming read mutex."); - } -} - -void -Serial::SerialImpl::readUnlock() -{ - if (!ReleaseMutex(read_mutex)) { - THROW (IOException, "Error releasing read mutex."); - } -} - -void -Serial::SerialImpl::writeLock() -{ - if (WaitForSingleObject(write_mutex, INFINITE) != WAIT_OBJECT_0) { - THROW (IOException, "Error claiming write mutex."); - } -} - -void -Serial::SerialImpl::writeUnlock() -{ - if (!ReleaseMutex(write_mutex)) { - THROW (IOException, "Error releasing write mutex."); - } -} - -#endif // #if defined(_WIN32) - diff --git a/serial/src/serial.cc b/serial/src/serial.cc deleted file mode 100644 index a9e6f84..0000000 --- a/serial/src/serial.cc +++ /dev/null @@ -1,432 +0,0 @@ -/* Copyright 2012 William Woodall and John Harrison */ -#include - -#if !defined(_WIN32) && !defined(__OpenBSD__) && !defined(__FreeBSD__) -# include -#endif - -#if defined (__MINGW32__) -# define alloca __builtin_alloca -#endif - -#include "serial/serial.h" - -#ifdef _WIN32 -#include "serial/impl/win.h" -#else -#include "serial/impl/unix.h" -#endif - -using std::invalid_argument; -using std::min; -using std::numeric_limits; -using std::vector; -using std::size_t; -using std::string; - -using serial::Serial; -using serial::SerialException; -using serial::IOException; -using serial::bytesize_t; -using serial::parity_t; -using serial::stopbits_t; -using serial::flowcontrol_t; - -class Serial::ScopedReadLock { -public: - ScopedReadLock(SerialImpl *pimpl) : pimpl_(pimpl) { - this->pimpl_->readLock(); - } - ~ScopedReadLock() { - this->pimpl_->readUnlock(); - } -private: - // Disable copy constructors - ScopedReadLock(const ScopedReadLock&); - const ScopedReadLock& operator=(ScopedReadLock); - - SerialImpl *pimpl_; -}; - -class Serial::ScopedWriteLock { -public: - ScopedWriteLock(SerialImpl *pimpl) : pimpl_(pimpl) { - this->pimpl_->writeLock(); - } - ~ScopedWriteLock() { - this->pimpl_->writeUnlock(); - } -private: - // Disable copy constructors - ScopedWriteLock(const ScopedWriteLock&); - const ScopedWriteLock& operator=(ScopedWriteLock); - SerialImpl *pimpl_; -}; - -Serial::Serial (const string &port, uint32_t baudrate, serial::Timeout timeout, - bytesize_t bytesize, parity_t parity, stopbits_t stopbits, - flowcontrol_t flowcontrol) - : pimpl_(new SerialImpl (port, baudrate, bytesize, parity, - stopbits, flowcontrol)) -{ - pimpl_->setTimeout(timeout); -} - -Serial::~Serial () -{ - delete pimpl_; -} - -void -Serial::open () -{ - pimpl_->open (); -} - -void -Serial::close () -{ - pimpl_->close (); -} - -bool -Serial::isOpen () const -{ - return pimpl_->isOpen (); -} - -size_t -Serial::available () -{ - return pimpl_->available (); -} - -bool -Serial::waitReadable () -{ - serial::Timeout timeout(pimpl_->getTimeout ()); - return pimpl_->waitReadable(timeout.read_timeout_constant); -} - -void -Serial::waitByteTimes (size_t count) -{ - pimpl_->waitByteTimes(count); -} - -size_t -Serial::read_ (uint8_t *buffer, size_t size) -{ - return this->pimpl_->read (buffer, size); -} - -size_t -Serial::read (uint8_t *buffer, size_t size) -{ - ScopedReadLock lock(this->pimpl_); - return this->pimpl_->read (buffer, size); -} - -size_t -Serial::read (std::vector &buffer, size_t size) -{ - ScopedReadLock lock(this->pimpl_); - uint8_t *buffer_ = new uint8_t[size]; - size_t bytes_read = 0; - - try { - bytes_read = this->pimpl_->read (buffer_, size); - } - catch (const std::exception &e) { - delete[] buffer_; - throw; - } - - buffer.insert (buffer.end (), buffer_, buffer_+bytes_read); - delete[] buffer_; - return bytes_read; -} - -size_t -Serial::read (std::string &buffer, size_t size) -{ - ScopedReadLock lock(this->pimpl_); - uint8_t *buffer_ = new uint8_t[size]; - size_t bytes_read = 0; - try { - bytes_read = this->pimpl_->read (buffer_, size); - } - catch (const std::exception &e) { - delete[] buffer_; - throw; - } - buffer.append (reinterpret_cast(buffer_), bytes_read); - delete[] buffer_; - return bytes_read; -} - -string -Serial::read (size_t size) -{ - std::string buffer; - this->read (buffer, size); - return buffer; -} - -size_t -Serial::readline (string &buffer, size_t size, string eol) -{ - ScopedReadLock lock(this->pimpl_); - size_t eol_len = eol.length (); - uint8_t *buffer_ = static_cast - (alloca (size * sizeof (uint8_t))); - size_t read_so_far = 0; - while (true) - { - size_t bytes_read = this->read_ (buffer_ + read_so_far, 1); - read_so_far += bytes_read; - if (bytes_read == 0) { - break; // Timeout occured on reading 1 byte - } - if(read_so_far < eol_len) continue; - if (string (reinterpret_cast - (buffer_ + read_so_far - eol_len), eol_len) == eol) { - break; // EOL found - } - if (read_so_far == size) { - break; // Reached the maximum read length - } - } - buffer.append(reinterpret_cast (buffer_), read_so_far); - return read_so_far; -} - -string -Serial::readline (size_t size, string eol) -{ - std::string buffer; - this->readline (buffer, size, eol); - return buffer; -} - -vector -Serial::readlines (size_t size, string eol) -{ - ScopedReadLock lock(this->pimpl_); - std::vector lines; - size_t eol_len = eol.length (); - uint8_t *buffer_ = static_cast - (alloca (size * sizeof (uint8_t))); - size_t read_so_far = 0; - size_t start_of_line = 0; - while (read_so_far < size) { - size_t bytes_read = this->read_ (buffer_+read_so_far, 1); - read_so_far += bytes_read; - if (bytes_read == 0) { - if (start_of_line != read_so_far) { - lines.push_back ( - string (reinterpret_cast (buffer_ + start_of_line), - read_so_far - start_of_line)); - } - break; // Timeout occured on reading 1 byte - } - if(read_so_far < eol_len) continue; - if (string (reinterpret_cast - (buffer_ + read_so_far - eol_len), eol_len) == eol) { - // EOL found - lines.push_back( - string(reinterpret_cast (buffer_ + start_of_line), - read_so_far - start_of_line)); - start_of_line = read_so_far; - } - if (read_so_far == size) { - if (start_of_line != read_so_far) { - lines.push_back( - string(reinterpret_cast (buffer_ + start_of_line), - read_so_far - start_of_line)); - } - break; // Reached the maximum read length - } - } - return lines; -} - -size_t -Serial::write (const string &data) -{ - ScopedWriteLock lock(this->pimpl_); - return this->write_ (reinterpret_cast(data.c_str()), - data.length()); -} - -size_t -Serial::write (const std::vector &data) -{ - ScopedWriteLock lock(this->pimpl_); - return this->write_ (&data[0], data.size()); -} - -size_t -Serial::write (const uint8_t *data, size_t size) -{ - ScopedWriteLock lock(this->pimpl_); - return this->write_(data, size); -} - -size_t -Serial::write_ (const uint8_t *data, size_t length) -{ - return pimpl_->write (data, length); -} - -void -Serial::setPort (const string &port) -{ - ScopedReadLock rlock(this->pimpl_); - ScopedWriteLock wlock(this->pimpl_); - bool was_open = pimpl_->isOpen (); - if (was_open) close(); - pimpl_->setPort (port); - if (was_open) open (); -} - -string -Serial::getPort () const -{ - return pimpl_->getPort (); -} - -void -Serial::setTimeout (serial::Timeout &timeout) -{ - pimpl_->setTimeout (timeout); -} - -serial::Timeout -Serial::getTimeout () const { - return pimpl_->getTimeout (); -} - -void -Serial::setBaudrate (uint32_t baudrate) -{ - pimpl_->setBaudrate (baudrate); -} - -uint32_t -Serial::getBaudrate () const -{ - return uint32_t(pimpl_->getBaudrate ()); -} - -void -Serial::setBytesize (bytesize_t bytesize) -{ - pimpl_->setBytesize (bytesize); -} - -bytesize_t -Serial::getBytesize () const -{ - return pimpl_->getBytesize (); -} - -void -Serial::setParity (parity_t parity) -{ - pimpl_->setParity (parity); -} - -parity_t -Serial::getParity () const -{ - return pimpl_->getParity (); -} - -void -Serial::setStopbits (stopbits_t stopbits) -{ - pimpl_->setStopbits (stopbits); -} - -stopbits_t -Serial::getStopbits () const -{ - return pimpl_->getStopbits (); -} - -void -Serial::setFlowcontrol (flowcontrol_t flowcontrol) -{ - pimpl_->setFlowcontrol (flowcontrol); -} - -flowcontrol_t -Serial::getFlowcontrol () const -{ - return pimpl_->getFlowcontrol (); -} - -void Serial::flush () -{ - ScopedReadLock rlock(this->pimpl_); - ScopedWriteLock wlock(this->pimpl_); - pimpl_->flush (); -} - -void Serial::flushInput () -{ - ScopedReadLock lock(this->pimpl_); - pimpl_->flushInput (); -} - -void Serial::flushOutput () -{ - ScopedWriteLock lock(this->pimpl_); - pimpl_->flushOutput (); -} - -void Serial::sendBreak (int duration) -{ - pimpl_->sendBreak (duration); -} - -void Serial::setBreak (bool level) -{ - pimpl_->setBreak (level); -} - -void Serial::setRTS (bool level) -{ - pimpl_->setRTS (level); -} - -void Serial::setDTR (bool level) -{ - pimpl_->setDTR (level); -} - -bool Serial::waitForChange() -{ - return pimpl_->waitForChange(); -} - -bool Serial::getCTS () -{ - return pimpl_->getCTS (); -} - -bool Serial::getDSR () -{ - return pimpl_->getDSR (); -} - -bool Serial::getRI () -{ - return pimpl_->getRI (); -} - -bool Serial::getCD () -{ - return pimpl_->getCD (); -}