简体   繁体   中英

Boost ASIO, async_read_some callback not called

My code works for read_some , but not for async_read_some . The data I'm reading is 5 chars long, whereas MAX_RESPONSE_SIZE 256 . I call async_read_some once from my main after opening the port, but the callback is never called after I swipe my prox card a few times. I have tried adding io_service.run() after async_read_some but it did not help. Am I missing something? Thank you.

header

boost::system::error_code error;
boost::asio::io_service io_service;
typedef boost::shared_ptr<boost::asio::serial_port> serial_port_ptr;
serial_port_ptr serial_port;
char read_buffer[MAX_RESPONSE_SIZE];

open

serial_port.reset();
serial_port = serial_port_ptr(new boost::asio::serial_port(io_service));
serial_port->open(device_path, error);

serial_port->set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
serial_port->set_option(boost::asio::serial_port_base::character_size(8));
serial_port->set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
serial_port->set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
serial_port->set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));

boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));

read

serial_port->async_read_some(
            boost::asio::buffer(read_buffer, MAX_RESPONSE_SIZE),
            boost::bind(
                &serial_comm::data_received,
                this, boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred
            )
        );

callback

void serial_comm::data_received(const boost::system::error_code& error, size_t bytes_transferred)
{
    // do stuff
}

You must ensure that there is always work to do, so that io_service::run() does not return and complete the thread where it is running.

As mentioned in the comments, you can create an io_service::work. However, I consider this artificial, a symptom of a design problem.

The better answer, probably, is that in the data_received handler, you should prepare for the next read if no fatal error occurred

void serial_comm::data_received(
  const boost::system::error_code& error,
  size_t bytes_transferred)
{
    // do stuff

   if( any_kind_of_fatal_error )
   {
       // return without setting up the next read
       // this will end reading
      return;
   }

   // the last read was successful
   // so setup for the next
   serial_port->async_read_some(
        boost::asio::buffer(read_buffer, MAX_RESPONSE_SIZE),
        boost::bind(
            &serial_comm::data_received,
            this, boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred
        )
    );

}

Basically my problem was not starting the io_service thread after async_read_some in the same function. Can you blame me? This stuff is not very clear cut. Here's my code in case anyone wants it (INFO and ERROR come from boost logging, see one of my other questions on it):

serial_comm.hpp

#ifndef __SERIAL_COMM_HPP
#define __SERIAL_COMM_HPP

#include <boost/asio.hpp>
#include <boost/asio/serial_port.hpp>
#include <boost/system/error_code.hpp>
#include <boost/system/system_error.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>

#include <string>
#include <atomic>

#include "logging.hpp"  // Boost logging

#define MAX_RESPONSE_SIZE 256

class serial_comm
{
    public:
        void open_serial_port (std::string device_path, unsigned int baud_rate);
        void close_serial_port (void);
        void async_read_some (void);

        std::string serial_read_data;
        std::atomic <bool> serial_data_read_complete{false};
    private:
        // functions
        void data_received (const boost::system::error_code& ec, size_t bytes_transferred);

        // variables
        boost::mutex mutex;
        boost::system::error_code error;
        boost::asio::io_service io_service;
        typedef boost::shared_ptr<boost::asio::serial_port> serial_port_ptr;
        serial_port_ptr serial_port;
        char read_buffer[MAX_RESPONSE_SIZE];
};

#endif  // __SERIAL_COMM_HPP

serial_comm.cpp

#include "../include/serial_comm.hpp"

void serial_comm::open_serial_port (std::string device_path, unsigned int baud_rate)
{   
    INFO << "started";

    try
    {
        serial_port.reset();
        serial_port = serial_port_ptr(new boost::asio::serial_port(io_service));
        serial_port->open(device_path, error);
        if (error) 
        {
            ERROR << "error.message() >> " << error.message().c_str();
            throw -3;
        }
        // set options
        serial_port->set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
        serial_port->set_option(boost::asio::serial_port_base::character_size(8));
        serial_port->set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
        serial_port->set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
        serial_port->set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));    

    }
    catch (int error)
    {
        ERROR << "error = " << error;
        throw -1;
    }
    catch (const std::exception &e)
    {
        ERROR << "e.what() = " << e.what();
        throw -2;
    }

    INFO << device_path << " opened correctly";     
    INFO << "ended";

    return;
}

void serial_comm::close_serial_port()
{
    boost::mutex::scoped_lock lock(mutex);  // prevent multiple thread access

    INFO << "started";

    try
    {
        if (serial_port)
        {
            serial_port->cancel();
            serial_port->close();
            serial_port.reset();
        }
        else 
        {
            WARNING << "serial port is not open";
        }
        io_service.stop();
        io_service.reset();
    }
    catch (const std::exception &e)
    {
        ERROR << "e.what() = " << e.what();
        throw -1;
    }

    INFO << "ended";

    return;
}

void serial_comm::async_read_some (void)
{
    boost::mutex::scoped_lock lock (mutex); // prevent multiple threads

    INFO << "started";

    std::string data;

    try
    {
        if (serial_port.get() == NULL || !serial_port->is_open())
        {
            WARNING << "serial port is not open";
            throw -2;
        }

        serial_port->async_read_some(
            boost::asio::buffer(read_buffer, MAX_RESPONSE_SIZE),
            boost::bind(
                &serial_comm::data_received,
                this, boost::asio::placeholders::error, 
                boost::asio::placeholders::bytes_transferred
            )
        );
        // start io_service run thread after giving it work
        boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
    }
    catch (const std::exception &e)
    {
        ERROR << "e.what() = " << e.what();
        throw -1;
    }

    INFO << "ended";

    return;
}

void serial_comm::data_received(const boost::system::error_code& error, size_t bytes_transferred)
{
    boost::mutex::scoped_lock lock(mutex);  // prevent multiple thread access

    INFO << "started";

    try
    {
        if (serial_port.get() == NULL || !serial_port->is_open())
        {
            WARNING << "serial port is not open";
            throw -2;
        }
        if (error) 
        {
            ERROR << "error.message() >> " << error.message().c_str();
            throw -3;
        }           
        for (unsigned int i = 0; i < bytes_transferred; ++i) {
            serial_read_data += read_buffer[i];
        }       
        INFO << "bytes_transferred = " << bytes_transferred << "; serial_read_data = " << serial_read_data; 
        serial_data_read_complete = true;
    }
    catch (const std::exception &e)
    {
        ERROR << "e.what() = " << e.what();
        throw -1;
    }

    // prevent io_service from returning due to lack of work    
    serial_port->async_read_some(
        boost::asio::buffer(read_buffer, MAX_RESPONSE_SIZE),
        boost::bind(
            &serial_comm::data_received,
            this, boost::asio::placeholders::error, 
            boost::asio::placeholders::bytes_transferred
        )
    );

    INFO << "ended";

    return;
}

main.cpp

#include "../include/serial_comm.hpp"

int main(void) 
{
    serial_comm _serial_comm;

    try
    {       
        _serial_comm.open_serial_port("/dev/ttyS0", 9600);
        _serial_comm.async_read_some(); // this function will always check for data
        loop:       
        while (!_serial_comm.serial_data_read_complete)
        {
            sleep(1);
        }
        INFO << "_serial_comm.serial_read_data = " << _serial_comm.serial_read_data;
        _serial_comm.serial_read_data.clear();
        _serial_comm.serial_data_read_complete = false;
        goto loop;      
    }
    catch (int error)
    {
        ERROR << "error >> " << error;
        return;
    }

    FATAL << "main ended";

    return;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM