我正在使用Boost.Asio通过RS232与设备通信。我使用boost::asio::write()
发送命令,然后等待boost::asio::async_read()
和boost::asio::transfer_at_least(6)
完成条件的答案。有时,读取操作会成功完成,并且会读取6
个字节的数据。但是,还有一些情况下,读取操作会在成功时立即意外完成,并传输0
个字节。
我的理解是完成条件transfer_at_least
应该阻止读取操作完成,直到读取6
个字节为止。它显然不......为什么?我的临时解决方案是,如果没有传输字节,则从串口再次读取。对我来说这似乎是一个讨厌的解决方案。
我正在使用集成在类中的以下函数:
void SerialPort::do_read()
{
printf("reading feedback on the serial port \n");
boost::asio::async_read(port,boost::asio::buffer(&buffer_read,6), boost::asio::transfer_at_least(6), boost::bind(&SerialPort::read_callback, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
timeout_timer.expires_from_now(boost::posix_time::milliseconds(SERIAL_TIMEOUT_MS));
timeout_timer.async_wait(boost::bind(&SerialPort::wait_callback, this, boost::asio::placeholders::error));
printf("start event loop\n");
io_service.run();
printf("close event loop \n..................\n");
ready_to_write = true;
}
void SerialPort::read_callback(const boost::system::error_code& error, std::size_t bytes_transferred)
{
//*** DEBUG
printf("Function read_callback\n");
if(!error)
cout << "Error Code: " << error.message() <<endl;
cout << "Bytes transferred: " << bytes_transferred << endl;
//***
if(!error && bytes_transferred == 0)
{
//boost::asio::async_read(port,boost::asio::buffer(&buffer_read,6), boost::asio::transfer_at_least(6), boost::bind(&SerialPort::read_callback, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
printf("Serial Link : No data to read.\n");
return;
}
if(error)
{
printf("Serial Link : Error when reading data.\n");
return;
}
printf("Going to cancel the timer\n");
timeout_timer.cancel(); // will cause wait_callback to fire with an error boost::asio::error::operation_aborted
printf("%u %u %u %u %u %u\n", buffer_read[0], buffer_read[1], buffer_read[2], buffer_read[3], buffer_read[4], buffer_read[5]);
return;
}
void SerialPort::wait_callback(const boost::system::error_code& error) //Called when the timer's deadline expire
{
if (error)
{
printf("Timeout cancelled.\n");
return;
}
printf("Timeout fired.\n");
timer_has_fired = true;
printf("Going to cancel the serial port \n");
port.cancel(); // Close all asynchronous operation with serial port. will cause read_callback to fire with an error
printf("All operations on serial port cancelled.\n");
return;
}
buffer_read
是该类的成员,在我的头文件中创建:
unsigned char buffer_read[6];
启用处理程序跟踪并且程序运行正常时,我得到以下输出:
@asio|1412962730.893007|0*1|handle@000000000027EC60.async_read_some
@asio|1412962730.905008|0*2|deadline_timer@000000000027EC60.async_wait
@asio|1412962730.927009|>1|ec=system:0,bytes_transferred=6
@asio|1412962730.934009|1|deadline_timer@000000000027EC60.cancel
@asio|1412962730.941010|<1|
@asio|1412962730.944010|>2|ec=system:995
@asio|1412962730.948010|<2|
当它工作不正常时,我得到这个输出:
@asio|1412963195.973608|0*3|handle@000000000027EC60.async_read_some
@asio|1412963195.973608|0*4|deadline_timer@000000000027EC60.async_wait
@asio|1412963195.976608|>3|ec=system:0,bytes_transferred=0
@asio|1412963195.981608|<3|
@asio|1412963196.973665|>4|ec=system:0
@asio|1412963196.976665|4|handle@000000000027EC60.cancel
@asio|1412963196.977665|<4|
我的打印语句结果为:
Reading feedback on the serial port start event loop Function read_callback Error Code: The operation completed successfully Bytes transferred: 0 Serial Link : No data to read Timeout fired. Going to cancel the serial port All operations on serial port cancelled close event loop
我能够使用以下代码重现针对x64平台编译的问题。串行通道上没有数据(设备不发送任何内容,因此async_read
功能不应调用read_callback
功能。)
#define BOOST_ASIO_ENABLE_HANDLER_TRACKING
#include <boost\asio.hpp>
#include <boost\bind.hpp>
#include <boost\thread.hpp>
#include <stdexcept>
#include <iostream>
#include <string>
using namespace std;
class SerialPort
{
public:
SerialPort(boost::asio::io_service& io_service, unsigned int baud, const string& peripheral) :
io_service(io_service), port(io_service, peripheral)
{
if(!port.is_open())
{
throw std::runtime_error("The serial port is already open.");
return;
}
port.set_option(boost::asio::serial_port_base::baud_rate(baud));
cout << ".. Serial Port " << peripheral << " opened" << endl;
do_read();
}
void close()
{
do_close(boost::system::error_code());
}
private:
boost::asio::io_service& io_service;
boost::asio::serial_port port;
unsigned char buffer_read[6];
void do_read()
{
boost::asio::async_read(port,boost::asio::buffer(buffer_read, 6), boost::asio::transfer_at_least(6), boost::bind(&SerialPort::read_callback, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
void read_callback(const boost::system::error_code& error, std::size_t bytes_transferred)
{
if(error)
{
return;
}
if(!error && bytes_transferred == 0)
{
cout << "Unexpected behaviour: no error but 0 byte transferred. \n" << endl;
return;
}
do_read();
}
void do_close(const boost::system::error_code& error)
{
if(error)
cerr << "Error detected: " << error.message() << endl;
port.close();
cout << ".. Serial Port closed" << endl;
return;
}
};
int main(int argc, char* argv[])
{
try
{
boost::asio::io_service io_service;
SerialPort motor(io_service, 9600, "COM5");
boost::thread thread_motor(boost::bind(&boost::asio::io_service::run, &io_service));
system("pause");
motor.close();
thread_motor.join();
}
catch (std::exception& e)
{
std::cerr << "Exception caught: " << e.what() << '\n';
}
system("pause");
return 0;
}
修改 我花了一些时间来改进我的代码。我用以下代码重现了这个问题。我正在使用Boost 1.56.0并编译为x64平台(Windows 7)。我删除了系统(“暂停”),我试图找到一种更好的方法来使用io_service。
对于串口,我曾经有一个特定的串口设备。为了便于重现,我编写了一个非常简单的代码(在Arduino Due上运行)。
有趣的是,async_read的第一次调用以我正在谈论的奇怪行为结束,第二次调用等待数据到达(所以没关系!)。
请随意评论我的代码:
#define BOOST_ASIO_ENABLE_HANDLER_TRACKING
#define SERIAL_TIMEOUT_MS 10000
#include <boost\asio.hpp>
#include <boost\bind.hpp>
#include <boost\thread.hpp>
#include <boost/noncopyable.hpp>
#include <stdexcept>
#include <iostream>
#include <string>
using namespace std;
class SerialPort : private boost::noncopyable
{
public:
SerialPort(boost::asio::io_service& io_service, unsigned int baud, const string& peripheral) :
io_service(io_service), port(io_service, peripheral), timeout_timer(io_service)
{
if(!port.is_open())
{
throw std::runtime_error("The serial port is already open.");
return;
}
port.set_option(boost::asio::serial_port_base::baud_rate(baud));
cout << "Serial Port " << peripheral << " opened." << endl;
}
void close()
{
io_service.post(boost::bind(&SerialPort::do_close, this, boost::system::error_code()));
}
void do_read()
{
cout << "..Do_read called." << endl;
boost::asio::async_read(port,boost::asio::buffer(buffer_read, 6), boost::asio::transfer_at_least(6), boost::bind(&SerialPort::read_callback, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
timeout_timer.expires_from_now(boost::posix_time::milliseconds(SERIAL_TIMEOUT_MS));
timeout_timer.async_wait(boost::bind(&SerialPort::wait_callback, this, boost::asio::placeholders::error));
}
private:
boost::asio::io_service& io_service;
boost::asio::serial_port port;
boost::asio::deadline_timer timeout_timer;
unsigned char buffer_read[6];
void read_callback(const boost::system::error_code& error, std::size_t bytes_transferred)
{
cout << "..Read_callback called." << endl;
if(error)
{
cerr << "Read_callback called with an error." << endl;
do_close(error);
return;
}
if(!error && bytes_transferred == 0)
{
cerr << ".. ..Unexpected behaviour: no error but 0 byte transferred. \n" << endl;
//timeout_timer.cancel(); // THIS LINE SHOULD BE INSTEAD OF THE NEXT ONE
do_read(); // NASTY SOLUTION TO GET IT WORKING
return;
}
cout << ".. ..Bytes transferred: " << bytes_transferred << " bytes." << endl;
cout << ".. ..Message: " << buffer_read[0] << buffer_read[1] << buffer_read[2] << buffer_read[3] << buffer_read[4] << buffer_read[5] << endl;
timeout_timer.cancel();
do_read();
}
void wait_callback(const boost::system::error_code& error) //Called when the timer's deadline expire
{
cout << "..Wait_callback called." << endl;
if (error)
{
cout << ".. ..Timeout cancelled." << endl;
if(error != boost::asio::error::operation_aborted) // if timer fired
do_close(error); // close the serial port only if error is not "operation_aborted"
return;
}
cout << ".. ..Timeout fired." << endl;
port.cancel(); // Close all asynchronous operation with serial port. will cause read_callback to fire with an error
// Does io_service keep running ?
cout << ".. ..All operations on serial port cancelled.\n" <<endl;
return;
}
void do_close(const boost::system::error_code& error)
{
cout << "..Do_close called" << endl;
if(error)
cerr << "Error detected: " << error.message() << endl;
if(error == boost::asio::error::operation_aborted)
return;
timeout_timer.cancel();
port.close();
cout << "Serial Port closed" << endl;
return;
}
};
int main(int argc, char* argv[])
{
try
{
boost::asio::io_service io_service;
auto_ptr<boost::asio::io_service::work> work(new boost::asio::io_service::work(io_service));
// to prevent io_service to stop if no work left
SerialPort motor(io_service, 9600, "COM6");
boost::thread thread_motor(boost::bind(&boost::asio::io_service::run, &io_service));
motor.do_read();
cin.get();
motor.close(); // close the serial port
work.reset(); // wait for handlers to finish normally. io_service stops when no more work to do.
thread_motor.join(); // delete the current thread
}
catch (std::exception& e)
{
std::cerr << "Exception caught: " << e.what() << '\n';
}
cout <<"Press any key to exit..."<<endl;
cin.get();
return 0;
}
Arduino代码:
void setup()
{
Serial.begin(9600);
}
void loop()
{
delay(5000);
Serial.write("azerty");
}
非常感谢你告诉我我做错了什么!
亚历