I'm developing Serial Port program using Boost::Asio.
I call the SerialPort::read_async
method every time I want to read data from serial port.
While I am testing I realized that the data received on serial port is not getting saved in the read_buffer
however the read handler receives proper number of received bytes in boost::asio::placeholders::bytes_transferred
field/parameter. The read handler also contains boost::system::errc::success
in the boost::asio::placeholders::error
field/parameter.
The read_buffer
holds exactly the same value that was set before the async_read_some
call was made.
this->read_buffer.fill(static_cast<std::byte>('\0')); //Clear Buffer
this->read_buffer.fill(static_cast<std::byte>('0')); //For Testing
Code
bool SerialPort::read_async(std::uint32_t read_timeout)
{
try
{
this->read_buffer.fill(static_cast<std::byte>('\0')); //Clear Buffer
//this->read_buffer.fill(static_cast<std::byte>('0')); //For Testing
if (read_timeout not_eq SerialPort::ignore_timeout)
this->read_timeout = read_timeout;//If read_timeout is not set to ignore_timeout, update the read_timeout else use old read_timeout
this->port.async_read_some(boost::asio::buffer(this->read_buffer.data(), this->read_buffer.size()),
boost::bind(&SerialPort::read_handler, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
return true;
}
catch (const std::exception& ex)
{
PLOG_ERROR << ex.what();
return false;
}
}
Update
Remaining Code
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());
}
}
CodePudding user response:
Can you show the/a complete, self-contained minimal example. The code shown has no obvious issue (except some smells like the argument read_timeout
soft-shadowing the member variable of the same name - and effectively being unused).
Here is a minimal self-contained example just from the code shown:
CodePudding user response:
I figured out the problem.
In my SerialPort::read_handler
method I was calling this->read_async()
before reading/copying the data from buffer.
this->read_async()
is resetting the buffer.
The thing I don't understand is why this is haappening at random? Is this a scheduling issue (i.e OS is causing context switching)?