Boost logo

Boost Users :

Subject: Re: [Boost-users] boost::io_service run() never returns
From: shunguang (shunguang_at_[hidden])
Date: 2013-07-05 10:43:59


Hi Igor,

Thanks for replying. I may oversimplify my original code in the post, but
this piece of code can run and read data from the port. The only problem is
unstable.

Here is my original code modify from a previous version posted on this
board. Again this program can successfully read data from a third part
device (which writes data to the RS232 port at certain sampling frequency),
but just unstable some times. I know you are a big guy in this discussing
board, any suggestions/comments for my debugging?

Thanks a lot!

-shunguang

//------------------ File: test_Rs232Client.cpp -----------------------
#include <stdio.h>
#include <conio.h>
#include <iostream>

#include "Rs232Client.h"

using namespace std;
using namespace boost::asio;

int main(int argc, char* argv[])
{
        try{

                SerialPortParams params; //default construct setting params

                boost::asio::io_service io;
                Rs232Client client(io, params);

                //todo: write sth to the port to control HMR2300 device
                //client.write( ... );

                client.set_max_cnt( 500 );
                client.set_dump_file( "c:\\temp\\t1.txt" );

                //read data from port into $m_readBuf$, this will read forever
                client.read();

                io.run();
        }
        catch (std::exception& e) {
                cerr << "Exception: " << e.what() << "\n";
        }
        return 0;
}

//------------------ File: SerialPortParams.h -----------------------

/*
SerialPortParams.h
*/
#ifndef SERIAL_PORT_PARAMS_H
#define SERIAL_PORT_PARAMS_H

#include <string>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>

class SerialPortParams
{
public:
        SerialPortParams()
                :device("COM1")
                ,baud_rate(9600)
                ,csize( 8 )
                ,flow( boost::asio::serial_port_base::flow_control::software )
                ,parity( boost::asio::serial_port_base::parity::none )
                ,stopbit( boost::asio::serial_port_base::stop_bits::one )
        {
        }

public:
        std::string device;
        boost::asio::serial_port_base::baud_rate baud_rate;
        boost::asio::serial_port_base::character_size csize;
        boost::asio::serial_port_base::flow_control flow;
        boost::asio::serial_port_base::parity parity;
        boost::asio::serial_port_base::stop_bits stopbit;
};

#endif

//------------------ File: Rs232Client.h -----------------------

/*
Rs232Client.h
*/
#ifndef RS232_CLIENT_H
#define RS232_CLIENT_H

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

#include "SerialPortParams.h"

#define MAX_READ_BYTES 512
#define BYTE_FMT_STR_LEN 28
class DataRaw
{
public:
        DataRaw( const boost::posix_time::ptime &curTime, std::deque<unsigned char>
&q, const size_t len )
                : m_pt( curTime )
        {
                for ( size_t i=0; i<BYTE_FMT_STR_LEN; ++i ){
                        m_rawStr[i] = q.front();
                        q.pop_front();
                }
        }

        void print()
        {
                std::cout &lt;&lt; boost::posix_time :: to_simple_string ( m_pt ) &lt;&lt;
std::endl;
                for ( size_t i=0; i&lt;BYTE_FMT_STR_LEN; ++i ){
                        printf( &quot;%c&quot;, m_rawStr[i] );
                }
                printf( &quot;\n&quot; );
        }

        void dump_to_file( FILE *fp ) const
        {
                if (fp){
                        std::string str = boost::posix_time::to_simple_string( m_pt );
                        fprintf( fp, &quot;%s,&quot;, str.c_str() );
                        for ( size_t i=0; i&lt;BYTE_FMT_STR_LEN; ++i ){
                                fprintf( fp, &quot;%d,&quot;, m_rawStr[i] );
                        }
                        fprintf(fp, &quot;\n&quot;);
                }
        }

public:
        boost::posix_time::ptime m_pt;
        unsigned char m_rawStr[BYTE_FMT_STR_LEN];
};

class Rs232Client
{
public:
        Rs232Client(boost::asio::io_service&amp; io_service, const SerialPortParams
&amp;p );
        virtual ~Rs232Client();
        
        void write(const char msg); // pass the write data to the do_write function
via the io service in the other thread
        void read(); //read data into $m_readMsg$ queue
        void close(); // call the do_close function via the io service in the
other thread

        bool active(); // return true if the serial port is still active
        void set_stop_signal() { m_stop = true; }
        void set_max_cnt( const size_t maxCnt ) { m_maxCnt = maxCnt ; }
        void set_dump_file( const std::string &amp;str ) { m_dumpedFileName = str;
}

private:
        void read_start();
        void read_complete(const boost::system::error_code&amp; error, size_t
bytes_transferred);
        void do_write(const char msg);
        void write_start();
        void write_complete(const boost::system::error_code&amp; error);
        void do_close(const boost::system::error_code&amp; error);
        void process_data( const size_t bytes_transferred );
        void dump_data( const char *file ) const;

public:
        std::vector&lt;DataRaw> m_vRaw; //the raw data with time stamps
        size_t m_cnt;

private:
        bool m_isActive; // remains true while this object is still operating
        boost::asio::io_service& m_ioService; // the main IO service that runs this
connection
        boost::asio::serial_port m_serialPort; // the serial port this instance is
connected to
        unsigned char m_readMsg[MAX_READ_BYTES];// one time data read from the
RS232 port by async_read_some()
        std::deque<unsigned char> m_writeBuf; // buffered data to write to the port
        std::deque<unsigned char> m_readBuf; // buffered data reading from the port
        bool m_stop; // stop communication if this flag is true

        size_t m_maxCnt; //the maximum # of cnt to stop reading/quiting
        std::string m_dumpedFileName;
};

#endif

//------------------ File: Rs232Client.cpp -----------------------
/*
Rs232Client.cpp
To end the application, send Ctrl-C on standard input
*/

#include "Rs232Client.h"
using namespace std;
using namespace boost;

Rs232Client :: Rs232Client(boost::asio::io_service& io, const
SerialPortParams &p )
        : m_isActive( true )
        , m_stop ( false )
        , m_ioService( io )
        , m_serialPort( io, p.device.c_str() )
        , m_cnt( 0 )
        , m_maxCnt( 0 )
        , m_dumpedFileName("")
{
        if ( !m_serialPort.is_open() )
        {
                cerr << "Failed to open serial port\n";
                return;
        }

        m_serialPort.set_option( p.baud_rate );
        m_serialPort.set_option( p.csize );
        m_serialPort.set_option( p.flow );
        m_serialPort.set_option( p.parity );
        m_serialPort.set_option( p.stopbit );

        //clear internal data queues
        m_writeBuf.clear();
        m_readBuf.clear();
}

Rs232Client::~Rs232Client()
{
        if (m_isActive){
                close();
        }
}

void Rs232Client :: read() // pass the write data to the do_write function
via the io service in the other thread
{
        printf("read() called \n");
        read_start();
}

void Rs232Client :: write(const char msg) // pass the write data to the
do_write function via the io service in the other thread
{
        m_ioService.post(boost::bind(&Rs232Client::do_write, this, msg));
}

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

bool Rs232Client ::active() // return true if the socket is still active
{
        return m_isActive;
}

void Rs232Client :: read_start()
{ // Start an asynchronous read and call read_complete when it completes or
fails
        m_serialPort.async_read_some( boost::asio::buffer(m_readMsg,
MAX_READ_BYTES),
                boost::bind( &Rs232Client::read_complete,
                this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred) );
}

void Rs232Client :: 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
        printf("%%%%%%%\n");
        if (!error && !m_stop ){
                // read completed, so push the data into queue
                process_data( bytes_transferred );

                //start waiting for another asynchronous read again
                read_start();

                //cnt the # of reading operations
                ++m_cnt;
                if ( m_cnt>m_maxCnt ){
                        m_stop = true;
                }

        }
        else{
                do_close(error);
        }
}

void Rs232Client :: dump_data( const char *file ) const
{
        size_t n = m_vRaw.size();

        FILE *fp=fopen( file, "w" );
        if ( fp== NULL ) {
                return;
        }

        fprintf( fp, "nSamples=%d\n", n );
        vector<DataRaw>::const_iterator it = m_vRaw.begin();
        for ( ; it != m_vRaw.end(); ++it ){
                it->dump_to_file( fp );
        }

        fclose( fp );

}

void Rs232Client :: process_data( const size_t bytes_transferred )
{
        //1. write reading data into $m_readBuf$
        //printf("%d,", bytes_transferred );
        for ( size_t i=0; i<bytes_transferred; ++i ){
                m_readBuf.push_back( m_readMsg[i] );
                //printf(&quot;%d,&quot;, m_readMsg[i] );
        }
        //printf(&quot;\n&quot;);

        //2. if there are more than $BYTE_FMT_STR_LEN$ characters in $readBuf$, the
move data to $m_vRaw$
        while ( m_readBuf.size() >= BYTE_FMT_STR_LEN ){
                
                posix_time::ptime pt( posix_time::second_clock::local_time() );

                //copy data from $m_readBuf$ to $raw$, and remove copied data from dequeue
                DataRaw raw( pt, m_readBuf, BYTE_FMT_STR_LEN );
                
                raw.print();

                //store data
                m_vRaw.push_back( raw );
        }
}

void Rs232Client :: do_write(const char msg)
{ // callback to handle write call from outside this class
        bool write_in_progress = !m_writeBuf.empty(); // is there anything
currently being written?
        printf( "do_write() called, write_in_progress=%d\n", write_in_progress );
        m_writeBuf.push_back(msg); // store in write buffer
        if (!write_in_progress) // if nothing is currently being written, then
start
                write_start();
}

void Rs232Client :: write_start(void)
{ // Start an asynchronous write and call write_complete when it completes
or fails
        boost::asio::async_write(m_serialPort,
                boost::asio::buffer(&m_writeBuf.front(), 1),
                boost::bind(&Rs232Client::write_complete,
                this,
                boost::asio::placeholders::error));
}

void Rs232Client :: 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
                m_writeBuf.pop_front(); // remove the completed data
                if (!m_writeBuf.empty()) // if there is anthing left to be written
                        write_start(); // then start sending the next item in the buffer
        }
        else{
                do_close(error);
        }
}

// something has gone wrong, so close the socket & make this object inactive
void Rs232Client :: do_close(const boost::system::error_code& error)
{
        if( ! m_dumpedFileName.empty() ){
                dump_data(m_dumpedFileName.c_str() );
        }
        //if this call is the result of a timer cancel()
        if (error == boost::asio::error::operation_aborted) {
                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";
        }

        m_serialPort.close();
        m_isActive = false;
}

--
View this message in context: http://boost.2283326.n4.nabble.com/boost-io-service-run-never-returns-tp4649306p4649400.html
Sent from the Boost - Users mailing list archive at Nabble.com.

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