[英]Boost::Asio not able to configure Windows & Linux serial port structures using native handle
我正在使用 Boost::Asio 处理串行端口例程。
我正在使用 Boost::Asio 提供的包装器配置端口。
在我的应用程序中,数据结束由接收超时或\r\n
行终止序列表示。
由于 Boost::Asio 不提供包装器来访问和配置DCB
- Windows, termios
- Linux 结构以配置超时和/或行结束; 我正在访问通过native_handle
包装器返回的本机端口句柄/文件描述符并手动配置结构。
但是,我似乎无法正确配置端口。
即使我将数据的结尾配置为用\n
表示,数据也会以块的形式部分返回。
同样,数据也会在超时发生之前返回。
更新
系统工作于 2 种模式
\r
、 \n
或\r\n
。代码
bool SerialPort::open_port(void)
{
try
{
this->port.open(this->port_name);
this->native_port = this->port.native_handle();
return true;
}
catch (const std::exception& ex)
{
PLOG_FATAL << ex.what();
}
return false;
}
bool SerialPort::open_port(const std::string& port_name, std::uint32_t baud_rate, std::uint8_t data_bits, std::uint8_t stop_bits,
parity_t parity, flow_control_t flow_control, std::uint32_t read_timeout, std::uint32_t read_inter_byte_timeout,
std::uint32_t write_timeout)
{
try
{
this->port_name = port_name;
if (not this->open_port())
return false;
if (not this->set_baud_rate(baud_rate).has_value())
return false;
if (not this->set_data_bits(data_bits).has_value())
return false;
if (not this->set_stop_bits(stop_bits).has_value())
return false;
if (not this->set_parity(parity).has_value())
return false;
if (not this->set_flow_control(flow_control).has_value())
return false;
this->read_timeout = read_timeout;
if (read_inter_byte_timeout <= 0)
this->read_inter_byte_timeout = 1;
#ifdef _WIN64
BOOL return_value;
DCB dcb = { 0 };
COMMTIMEOUTS timeouts = { 0 };
if (this->line_mode) //Set COM port to return data either at \n or \r
{
/*
* If the function succeeds, the return value is nonzero.
* If the function fails, the return value is zero. To get extended error information, call GetLastError.
*/
return_value = GetCommState(this->native_port, &dcb);
if (return_value)
{
if(this->new_line_character == '\r')
dcb.EofChar = '\r'; //Specify end of data character as carriage-return (\r)
else // --> Default
dcb.EofChar = '\n'; //Specify end of data character as new-line (\n)
}
else
{
PLOG_ERROR << "Error GetCommState : " << GetLastErrorAsString();
return false;
}
/*
* If the function succeeds, the return value is nonzero.
* If the function fails, the return value is zero. To get extended error information, call GetLastError.
*/
return_value = SetCommState(this->native_port, &dcb);
if (not return_value)
{
PLOG_ERROR << "Error SetCommState : " << GetLastErrorAsString();
return false;
}
}
else //Set COM port to return data on timeout
{
/*
* If the function succeeds, the return value is nonzero.
* If the function fails, the return value is zero. To get extended error information, call GetLastError.
*/
return_value = GetCommTimeouts(this->native_port, &timeouts);
if (return_value)
{
timeouts.ReadIntervalTimeout = this->read_inter_byte_timeout; // Timeout in miliseconds
//timeouts.ReadTotalTimeoutConstant = 0; //MAXDWORD; // in milliseconds - not needed
//timeouts.ReadTotalTimeoutMultiplier = 0; // in milliseconds - not needed
//timeouts.WriteTotalTimeoutConstant = 50; // in milliseconds - not needed
//timeouts.WriteTotalTimeoutMultiplier = write_timeout; // in milliseconds - not needed
}
else
{
PLOG_ERROR << "Error GetCommTimeouts : " << GetLastErrorAsString();
return false;
}
/*
* If the function succeeds, the return value is nonzero.
* If the function fails, the return value is zero. To get extended error information, call GetLastError.
*/
return_value = SetCommTimeouts(this->native_port, &timeouts);
if (not return_value)
{
PLOG_ERROR << "Error SetCommTimeouts : " << GetLastErrorAsString();
return false;
}
}
#else //For Linux termios
#endif // _WIN64
return true;
}
catch (const std::exception& ex)
{
PLOG_ERROR << ex.what();
return false;
}
}
void SerialPort::read_handler(const boost::system::error_code& error, std::size_t bytes_transferred)
{
this->read_async(); // I realized I was calling read_async before reading data
bool receive_complete{ false };
try
{
if (error not_eq boost::system::errc::success) //Error in serial port read
{
PLOG_ERROR << error.to_string();
this->async_signal.emit(this->port_number, SerialPortEvents::read_error, error.to_string());
return;
}
if (this->line_mode)
{
std::string temporary_recieve_data;
std::transform(this->read_buffer.begin(), this->read_buffer.begin() + bytes_transferred, //Data is added to temporary buffer
std::back_inserter(temporary_recieve_data), [](std::byte character) {
return static_cast<char>(character);
}
);
boost::algorithm::trim(temporary_recieve_data); // Trim handles space character, tab, carriage return, newline, vertical tab and form feed
//Data is further processed based on the Process logic
receive_complete = true;
}
else // Bulk-Data. Just append data to end of received_data string buffer.
// Wait for timeout to trigger recevive_complete
{
//Test Function
std::transform(this->read_buffer.begin(), this->read_buffer.begin() + bytes_transferred,
std::back_inserter(this->received_data), [](std::byte character) {
return static_cast<char>(character);
}
);
this->async_signal.emit(this->port_number, SerialPortEvents::read_data, this->received_data); //Data has been recieved send to server via MQTT
}
}
catch (const std::exception& ex)
{
PLOG_ERROR << ex.what();
this->async_signal.emit(this->port_number, SerialPortEvents::read_error, ex.what());
}
}
配套 Function
std::optional<std::uint32_t> SerialPort::set_baud_rate(std::uint32_t baud_rate)
{
boost::system::error_code error;
std::uint32_t _baud_rate = 1200;
switch (baud_rate)
{
case 1200:
case 2400:
case 4800:
case 9600:
case 115200:
_baud_rate = baud_rate;
break;
default:
_baud_rate = 1200;
break;
}
this->port.set_option(boost::asio::serial_port_base::baud_rate(_baud_rate), error);
if (error)
{
PLOG_FATAL << error.message();
return std::nullopt;
}
return baud_rate;
}
std::optional<std::uint8_t> SerialPort::set_data_bits(std::uint8_t data_bits)
{
boost::system::error_code error;
std::uint32_t _data_bits = 8;
switch (data_bits)
{
case 7:
case 8:
_data_bits = data_bits;
break;
default:
_data_bits = 8;
break;
}
this->port.set_option(boost::asio::serial_port_base::character_size(_data_bits), error);
if (error)
{
PLOG_FATAL << error.message();
return std::nullopt;
}
return data_bits;
}
std::optional<std::uint8_t> SerialPort::set_stop_bits(std::uint8_t stop_bits)
{
boost::system::error_code error;
switch (stop_bits)
{
case 1:
this->port.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one), error);
break;
case 2:
this->port.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::two), error);
break;
default:
this->port.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one), error);
break;
}
if (error)
{
PLOG_FATAL << error.message();
return std::nullopt;
}
return stop_bits;
}
std::optional<parity_t> SerialPort::set_parity(parity_t parity)
{
boost::system::error_code error;
switch (parity)
{
case Parity::none:
this->port.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none), error);
break;
case Parity::even:
this->port.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::even), error);
break;
case Parity::odd:
this->port.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::odd), error);
break;
default:
this->port.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none), error);
break;
}
if (error)
{
PLOG_FATAL << error.message();
return std::nullopt;
}
return parity;
}
std::optional<flow_control_t> SerialPort::set_flow_control(flow_control_t flow_control)
{
boost::system::error_code error;
switch (flow_control)
{
case FlowControl::none:
this->port.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none), error);
break;
case FlowControl::hardware:
this->port.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::hardware), error);
break;
case FlowControl::software:
this->port.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::software), error);
break;
default:
this->port.set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none), error);
break;
}
if (error)
{
PLOG_FATAL << error.message();
return std::nullopt;
}
return flow_control;
}
对您的意见:
传入的数据是多行的,因此我在串行端口结构中设置了读取超时 .netrval
这有什么意义。 无论哪种方式,在 Asio 中处理超时都没有真正的区别/
关于过载:
但我收到 E0304 no instance of overloaded function "boost::asio::async_read_until" matches the argument list 错误。
这是我的十便士:
async_read_until(port,
asio::dynamic_buffer(read_buffer),
"\r\n",
bind(&SerialPort::read_handler, this, error, bytes_transferred));
请注意,您需要动态缓冲区。 我能想到的最简单的事情就是接近你原来的样子:
std::vector<std::byte> read_buffer;
现在,我们将更新读取处理程序以擦除“已消耗”部分,因为接收缓冲区可能包含超出定界符的数据。
void read_handler(boost::system::error_code ec, size_t const bytes_transferred) {
std::cerr << "received " << bytes_transferred << " bytes (" << ec.message() << ")"
<< std::endl;
auto b = reinterpret_cast<char const*>(read_buffer.data()),
e = b + std::min(bytes_transferred, read_buffer.size());
if (std::all_of(
b, e, //
[](uint8_t ch) { return std::isspace(ch) || std::isgraph(ch); })) //
{
std::cerr << "ascii: " << quoted(std::string_view(b, e)) << std::endl;
} else {
std::cerr << "binary: ";
auto fmt = std::cerr.flags();
for (auto it = b; it != e; ++it) {
std::cerr << " " << std::hex << std::showbase << std::setfill('0')
<< std::setw(4) << static_cast<unsigned>(*it);
}
std::cerr.flags(fmt);
}
std::cerr << std::endl;
read_buffer.erase(begin(read_buffer), begin(read_buffer) + bytes_transferred);
if (!ec)
read_async(ignore_timeout);
}
完整列表: Live On Coliru
#include <boost/asio.hpp>
#include <boost/asio/serial_port.hpp>
#include <boost/bind/bind.hpp>
#include <iomanip>
#include <iostream>
#include <ranges>
namespace asio = boost::asio;
static inline std::ostream PLOG_ERROR(std::cerr.rdbuf());
struct SerialPort {
static constexpr uint32_t ignore_timeout = -1;
SerialPort(asio::any_io_executor ex, std::string dev) : port(ex, dev) {}
bool read_async(uint32_t timeout_override) {
try {
// not necessary: std::ranges::fill(read_buffer, std::byte{});
if (timeout_override not_eq SerialPort::ignore_timeout) {
read_timeout = timeout_override;
}
using namespace asio::placeholders;
async_read_until(port,
asio::dynamic_buffer(read_buffer),
"\r\n",
bind(&SerialPort::read_handler, this, error, bytes_transferred));
return true;
} catch (std::exception const& ex) {
PLOG_ERROR << ex.what() << std::endl;
return false;
}
}
private:
void read_handler(boost::system::error_code ec, size_t const bytes_transferred) {
std::cerr << "received " << bytes_transferred << " bytes (" << ec.message() << ")"
<< std::endl;
auto b = reinterpret_cast<char const*>(read_buffer.data()),
e = b + std::min(bytes_transferred, read_buffer.size());
if (std::all_of(
b, e, //
[](uint8_t ch) { return std::isspace(ch) || std::isgraph(ch); })) //
{
std::cerr << "ascii: " << quoted(std::string_view(b, e)) << std::endl;
} else {
std::cerr << "binary: ";
auto fmt = std::cerr.flags();
for (auto it = b; it != e; ++it) {
std::cerr << " " << std::hex << std::showbase << std::setfill('0')
<< std::setw(4) << static_cast<unsigned>(*it);
}
std::cerr.flags(fmt);
}
std::cerr << std::endl;
read_buffer.erase(begin(read_buffer), begin(read_buffer) + bytes_transferred);
if (!ec)
read_async(ignore_timeout);
}
uint32_t read_timeout = 10;
std::vector<std::byte> read_buffer;
asio::serial_port port;
};
int main(int argc, char** argv) {
asio::io_context ioc;
SerialPort sp(make_strand(ioc), argc > 1 ? argv[1] : "/dev/ttyS0");
sp.read_async(SerialPort::ignore_timeout);
ioc.run();
}
本地演示:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.