Home > Software engineering >  Boost::Asio Serial Port async_read_some not storing data in buffer
Boost::Asio Serial Port async_read_some not storing data in buffer

Time:12-03

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:

enter image description here

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)?

  • Related