    /* 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>
     
    #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),
                      io_service_(io_service),
                      serialPort(io_service, device)
    	      ,m_timeoutTimer(m_io_service, boost::posix_time::milliseconds(10000)) // init the timer
            {
                    if (not serialPort.is_open())
                    {
                            cerr << "Failed to open serial port\n";
                            return;
                    }
                    boost::asio::serial_port_base::baud_rate baud_option(baud);
                    serialPort.set_option(baud_option); // set the baud rate after the port has been opened
                    read_start();
		    m_timeoutTimer.async_wait(boost::bind(&minicom_client::ResetPacketTimer, this, _1, _2)); // start the timer
            }
           
            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 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
                    serialPort.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
		    // you can check here if the packet is complete. Since bytes_transferred is what you got. So is it enough or not.
		    MyOnReceivedData(MyHandle someHandle, numOfBytes);
		    read_start(); // start waiting for another asynchronous read again
		}
		else do_close(error);
	    } 

	    void MyOnReceivedData(MyHandle someHandle,int numOfBytes){
		ResetPacketTimer();// we reset the timer here. So each read call is monitored NOT the time of a complete read
		//check if whole packet was received
		if (wholePacket){    
		    MyOnReceivedPacket(someHandle);    
		}
	    }

	    void MyOnReceivedPacket(MyHandle someHandle){
		cout << "packet"
		clearBuffer(someHandle);   
	    }

	    void MyOnPacketTimeout(MyHandle someHandle){
		cout << "we're in";
		if (!bufferEmpty(someHandle)){
		    cout << "timeout";
		}
	    }

	    void ResetPacketTimer(MyHandle someHandle, const boost::system::error_code& error){
		MyOnPacketTimeout(someHandle);
		m_timeoutTimer.expires_at(m_cpldTimeoutTimer.expires_at() + boost::posix_time::milliseconds(10000)); // give the timer another  10000 millisecs
		m_timeoutTimer.async_wait(boost::bind(&minicom_client::ResetPacketTimer, this, _1));
	    }
            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(serialPort,
                            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 << "Press Enter to exit\n";
                    serialPort.close();
                    active_ = false;
            }

    private:
            bool active_; // remains true while this object is still operating
            boost::asio::io_service& io_service_; // the main IO service that runs this connection
            boost::asio::serial_port serialPort; // 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
	    boost::asio::deadline_timer m_timeoutTimer;
    };
     
    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
                    boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
                    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 == 3) // 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
            }
            catch (exception& e)
            {
                    cerr << "Exception: " << e.what() << "\n";
            }
    #ifdef POSIX // restore default buffering of standard input
            tcsetattr(0, TCSANOW, &stored_settings);
    #endif
            return 0;
    }

