diff --git a/Core/Serial/Serial_Coro.h b/Core/Serial/Serial_Coro.h new file mode 100644 index 0000000..2a8e44b --- /dev/null +++ b/Core/Serial/Serial_Coro.h @@ -0,0 +1,167 @@ +#pragma once + +#include "Serial.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace Psc::serial { + +class Serial_Coro : public Serial { +public: + bool open() + { + if (port_ && port_->is_open()) { + return true; + } + + port_ = std::make_unique(io_context_); + asio::error_code ec; + port_->open(normalize_port_name(serial_name), ec); + if (ec) { + return false; + } + + apply_options(ec); + if (ec) { + close(); + return false; + } + + serial::set_block(port_->native_handle(), false); + + return true; + } + + void close() + { + if (!port_) { + return; + } + + asio::error_code ec; + port_->cancel(ec); + port_->close(ec); + port_.reset(); + io_context_.restart(); + } + + [[nodiscard]] ucoro::awaitable tick_coro() + { + io_context_.poll(); + co_return; + } + + [[nodiscard]] ucoro::awaitable read_coro(std::size_t max_size = 16 * 1024) + { + co_await tick_coro(); + if (!port_ || !port_->is_open()) { + co_return ""; + } + + auto available = serial::get_available_bytes(port_->native_handle()); + if (available <= 0) { + co_return ""; + } + + auto data = serial::read_all(port_->native_handle()); + if (data.size() > max_size) { + data.resize(max_size); + } + co_return data; + } + + [[nodiscard]] ucoro::awaitable write_coro(std::string data) + { + co_await tick_coro(); + if (!port_ || !port_->is_open() || data.empty()) { + co_return 0; + } + + asio::error_code ec; + auto size = asio::write(*port_, asio::buffer(data), ec); + if (ec) { + co_return 0; + } + + co_return size; + } + + std::string read(int64_t size = -1) + { + auto max_size = size > 0 ? static_cast(size) : static_cast(16 * 1024); + return ucoro::sync_await(read_coro(max_size)); + } + + int64_t write(const std::string& data) + { + return static_cast(ucoro::sync_await(write_coro(data))); + } + + int get_available_bytes() + { + if (!port_ || !port_->is_open()) { + return 0; + } + return serial::get_available_bytes(port_->native_handle()); + } + +private: + static std::string normalize_port_name(std::string port_name) + { +#ifdef _WIN32 + if (port_name.rfind("\\\\.\\", 0) != 0) { + port_name = "\\\\.\\" + port_name; + } +#endif + return port_name; + } + + void apply_options(asio::error_code& ec) + { + if (baud_rate != static_cast(-1)) { + port_->set_option(asio::serial_port_base::baud_rate(static_cast(baud_rate)), ec); + if (ec) return; + } + + if (dataBits != DataBits::UnknownDataBits) { + port_->set_option(asio::serial_port_base::character_size(static_cast(dataBits)), ec); + if (ec) return; + } + + if (parity != Parity::UnknownParity) { + asio::serial_port_base::parity::type value = asio::serial_port_base::parity::none; + if (parity == Parity::EvenParity) value = asio::serial_port_base::parity::even; + if (parity == Parity::OddParity) value = asio::serial_port_base::parity::odd; + port_->set_option(asio::serial_port_base::parity(value), ec); + if (ec) return; + } + + if (stopBits != StopBits::UnknownStopBits) { + asio::serial_port_base::stop_bits::type value = asio::serial_port_base::stop_bits::one; + if (stopBits == StopBits::OneAndHalfStop) value = asio::serial_port_base::stop_bits::onepointfive; + if (stopBits == StopBits::TwoStop) value = asio::serial_port_base::stop_bits::two; + port_->set_option(asio::serial_port_base::stop_bits(value), ec); + if (ec) return; + } + + if (flow_control != FlowControl::UnknownFlowControl) { + asio::serial_port_base::flow_control::type value = asio::serial_port_base::flow_control::none; + if (flow_control == FlowControl::HardwareControl) value = asio::serial_port_base::flow_control::hardware; + if (flow_control == FlowControl::SoftwareControl) value = asio::serial_port_base::flow_control::software; + port_->set_option(asio::serial_port_base::flow_control(value), ec); + } + } + + asio::io_context io_context_; + std::unique_ptr port_; +}; + +} // namespace Psc::serial