Boost logo

Boost Users :

Subject: Re: [Boost-users] [asio] reopening of serial port fails - why?
From: Markus Werle (numerical.simulation_at_[hidden])
Date: 2009-03-09 05:46:03


Igor R <boost.lists <at> gmail.com> writes:

> It seems that you open your serial_port object in the constructor
> "minicom_client" wrapper. But you create it only once, before the loop
> starts, don't you? So after you explicitly close serial_port, no one
> opens it.

Ooops!
So many tries and errors until such a stupid error.
Now I got it.

Rules are:

- first start do_read(),
- then start the thread
- after termination of the thread an io_service_.reset()
  is mandatory. Otherwise subsequent thread starts lead to
  spurious behaviour. This was not clear to me and I dislike
  this behaviour ...
 
Working code below.

Markus

---snip---
// TestBoostAsio.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

//int _tmain(int argc, _TCHAR* argv[])
//{
// return 0;
//}

/* minicom.cpp
        A simple demonstration minicom client with Boost asio
        
        Parameters:
                baud rate
                serial port (eg /dev/ttyS0 or COM1)
                
        To end the application, send Ctrl-C on standard input
*/

#include <deque>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/asio/serial_port.hpp>
#include <boost/thread.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>

#include <iostream>

#ifdef POSIX
#include <termios.h>
#endif

using namespace std;

class minicom_client
{
public:
        minicom_client(boost::asio::io_service& io_service,
                                   unsigned int baud
                                        /*, const string& device */)
                : active_(true),
                  baudRate_(baud),
                  //device_(device),
                  io_service_(io_service),
                  pSerialPort_(new boost::asio::serial_port(io_service/* ,
device*/))
        {
                
        }
        
        void write(const char msg) // pass the write data to the do_write
function via the io service in the other thread
        {
                io_service_.post(boost::bind(&minicom_client::do_write, this,
msg));
        }
      
        void open(std::string const & portName)
        {
                pSerialPort_->open(portName);

                if (!pSerialPort_->is_open())
                {
                        cerr << "Failed to open serial port\n";
                        return;
                }
                boost::asio::serial_port_base::baud_rate baud_option
(baudRate_);
                pSerialPort_->set_option(baud_option); // set the baud rate
after the port has been opened
                read_start();
        }

        void close() // call the do_close function via the io service in the
other thread
        {
                io_service_.post(boost::bind(&minicom_client::do_close, this,
boost::system::error_code()));
        }

        bool active() // return true if the socket is still active
        {
                return active_;
        }

private:

        static const int max_read_length = 512; // maximum amount of data to
read in one operation
        
        void read_start(void)
        { // Start an asynchronous read and call read_complete when it
completes or fails
                pSerialPort_->async_read_some(boost::asio::buffer(read_msg_,
max_read_length),
                                                                   boost::bind
(&minicom_client::read_complete,
                                                                                
           this,
                                                                                
           boost::asio::placeholders::error,
                                                                                
           boost::asio::placeholders::bytes_transferred));
        }
        
        void read_complete(const boost::system::error_code& error, size_t
bytes_transferred)
        { // the asynchronous read operation has now completed or failed and
returned an error
                if (!error)
                { // read completed, so process the data
                        cout.write(read_msg_, bytes_transferred); // echo to
standard output
                        read_start(); // start waiting for another asynchronous
read again
                }
                else
                        do_close(error);
        }
        
        void do_write(const char msg)
        { // callback to handle write call from outside this class
                bool write_in_progress = !write_msgs_.empty(); // is there
anything currently being written?
                write_msgs_.push_back(msg); // store in write buffer
                if (!write_in_progress) // if nothing is currently being
written, then start
                        write_start();
        }
        
        void write_start(void)
        { // Start an asynchronous write and call write_complete when it
completes or fails
                boost::asio::async_write(*pSerialPort_,
                                                                 
boost::asio::buffer(&write_msgs_.front(), 1),
                                                                 boost::bind
(&minicom_client::write_complete,
                                                                                
         this,
                                                                                
         boost::asio::placeholders::error));
        }
        
        void write_complete(const boost::system::error_code& error)
        { // the asynchronous read operation has now completed or failed and
returned an error
                if (!error)
                { // write completed, so send next write data
                        write_msgs_.pop_front(); // remove the completed data
                        if (!write_msgs_.empty()) // if there is anthing left
to be written
                                write_start(); // then start sending the next
item in the buffer
                }
                else
                        do_close(error);
        }
        
        void do_close(const boost::system::error_code& error)
        { // something has gone wrong, so close the socket & make this object
inactive
                if (error == boost::asio::error::operation_aborted) // if this
call is the result of a timer cancel()
                        return; // ignore it because the connection cancelled
the timer
                if (error)
                        cerr << "Error: " << error.message() << endl; // show
the error message
                //else
                        //cout << "Error: Connection did not succeed.\n";
                cout << "Closing serial port\n";
                
                
                        
                pSerialPort_->close();
                
                /*if (pSerialPort_->is_open())
                {
                        delete pSerialPort_;
                        pSerialPort_ = new boost::asio::serial_port
(io_service_, device_);

                        boost::asio::serial_port_base::baud_rate baud_option
(baudRate_);
                        pSerialPort_->set_option(baud_option);
                }*/

                active_ = false;
        }

private:
        bool active_; // remains true while this object is still operating
        unsigned int const baudRate_;
        //const string& device_;

        boost::asio::io_service& io_service_; // the main IO service that runs
this connection
        boost::asio::serial_port * pSerialPort_; // the serial port this
instance is connected to
        char read_msg_[max_read_length]; // data read from the socket
        deque<char> write_msgs_; // buffered write data
};

int main(int argc, char* argv[])
{
// on Unix POSIX based systems, turn off line buffering of input, so cin.get()
returns after every keypress
// On other systems, you'll need to look for an equivalent
#ifdef POSIX
        termios stored_settings;
        tcgetattr(0, &stored_settings);
        termios new_settings = stored_settings;
        new_settings.c_lflag &= (~ICANON);
        new_settings.c_lflag &= (~ISIG); // don't automatically handle control-
C
        tcsetattr(0, TCSANOW, &new_settings);
#endif
        try
        {
                if (argc != 3)
                {
                        cerr << "Usage: minicom <baud> <device>\n";
                        return 1;
                }

                boost::asio::io_service io_service;
                // define an instance of the main class of this program
                minicom_client c(io_service, boost::lexical_cast<unsigned int>
(argv[1])/*, argv[2]*/);
                // run the IO service as a separate thread, so the main thread
can block on standard input
                
                while (true) // PROBLEM: reentering this loop fails - why?
                {
                        std::string port = argv[2];
                        c.open(port);
                        boost::thread t(boost::bind
(&boost::asio::io_service::run, &io_service));
                        
                        boost::asio::io_service io;
                        boost::asio::deadline_timer timer(io,
boost::posix_time::seconds(3));
                        timer.wait();

                        c.write('a');

                        boost::asio::deadline_timer timer2(io,
boost::posix_time::seconds(3));
                        timer2.wait();

                        //while (c.active()) // check the internal state of the
connection to make sure it's still running
                        //{
                        // char ch;
                        // cin.get(ch); // blocking wait for standard
input
                        // if (ch == 32) // ctrl-C to end program
                        // break;
                        // c.write(ch);
                        //}
                        c.close(); // close the minicom client connection
                        t.join(); // wait for the IO service thread to close
                        std::cout << "-------------------" << std::endl;
                        io_service.reset();
                }
        }
        catch (exception& e)
        {
                cerr << "Exception: " << e.what() << "\n";
        }
#ifdef POSIX // restore default buffering of standard input
        tcsetattr(0, TCSANOW, &stored_settings);
#endif
        return 0;
}


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net