Boost logo

Boost Users :

Subject: Re: [Boost-users] [asio::serial_port] I can read and write synchronously, but how do I unblock when I want to end?
From: Daniele Barzotti (daniele.barzotti_at_[hidden])
Date: 2012-02-16 02:52:54


Il 14/02/2012 22:02, Dario Ramos ha scritto:
>
> I'm seriously considering using async_read and async_write. Since I want
> read and write calls to be blocking, I'll need to use a Windows event or
> something. Is there a Boost class that is as fast, and less unwieldy?

Hi, I don't know if this can help you, but here my Serial Class which
uses async_read and write:

/////////////////////////////////////////////////////////////////////////////
// Name: Serial.h
// Purpose:
// Author: Daniele Barzotti
// Id: $Id: Serial.h,v 1.0.0.0 2009/02/-- jb Exp $
// Copyright: (c) 2009 Eurocom Telecomunicazioni
// Licence:
/////////////////////////////////////////////////////////////////////////////

#ifndef _SERIAL_H
#define _SERIAL_H

#include <boost/asio.hpp>
#include <boost/asio/serial_port.hpp>
#include <boost/thread.hpp>
#include <boost/signals2/signal.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

#if(WIN32 && _WIN32_DCOM)
  #include "ObjBase.h"
#endif

#define READ_TIMEOUT 200 //msec

#define wxCOM1 "com1"
#define wxCOM2 "com2"
#define wxCOM3 "com3"
#define wxCOM4 "com4"
#define wxCOM5 "com5"
#define wxCOM6 "com6"
#define wxCOM7 "com7"
#define wxCOM8 "com8"
#define wxCOM9 "com9"
#define wxCOM10 "\\\\.\\com10"
#define wxCOM11 "\\\\.\\com11"
#define wxCOM12 "\\\\.\\com12"
#define wxCOM13 "\\\\.\\com13"
#define wxCOM14 "\\\\.\\com14"
#define wxCOM15 "\\\\.\\com15"
#define wxCOM16 "\\\\.\\com16"

using namespace boost::asio;

/*!
  \enum SerialLineState

  Defines the different modem control lines.
  \see wxSerialLineState
*/
enum SerialLinesEnum
{
    /*! Data Carrier Detect (read only) */
    SERIAL_LINESTATE_DCD = 0x040,
    /*! Clear To Send (read only) */
    SERIAL_LINESTATE_CTS = 0x020,
    /*! Data Set Ready (read only) */
    SERIAL_LINESTATE_DSR = 0x100,
    /*! Data Terminal Ready (write only) */
    SERIAL_LINESTATE_DTR = 0x002,
    /*! Ring Detect (read only) */
    SERIAL_LINESTATE_RING = 0x080,
    /*! Request To Send (write only) */
    SERIAL_LINESTATE_RTS = 0x004,
    /*! no active line state, use this for clear */
    SERIAL_LINESTATE_NULL = 0x000
};

/*!
  \enum DevParity

  Defines the different modes of parity checking. Under
  Linux, the struct termios will be set to provide the wanted
  behaviour.
*/
enum DevParity
{
  /*! no parity check */
  devPARITY_NONE,
  /*! odd parity check */
  devPARITY_ODD,
  /*! even parity check */
  devPARITY_EVEN
};

/*!
  \enum DevFlowControl

  Defines the flow control to be used in RS232 communication.
*/
enum DevFlowControl
{
  /*! no flow control */
  FLOW_NONE,
  /*! hardware flow control */
  FLOW_RTS_CTS,
  /*! software flow control */
  FLOW_XON_XOFF,
  /*! hardware and software flow control */
  FLOW_RTS_XONOFF
};

/*!
  \struct SerialDCS

  The device control struct for the serial communication.
*/
struct SerialDCS {
  /*! the baudrate */
  INT baud;
  /*! the parity */
  DevParity parity;
  /*! count of stopbits */
  UCHAR stopbits;
  /*! flow control */
  DevFlowControl flowcontrol;

  // Constructor
  inline SerialDCS() {
    baud = 9600;
    parity = devPARITY_NONE;
    stopbits = 1;
    flowcontrol = FLOW_NONE;
  };
  // Destructor to avoid memory leak
  ~SerialDCS() {};
};

/*!
  \struct SerialLines

  This struct defines the read-only lines of RS232.
*/
struct TSerialLines {
  BOOL Line_DCD;
  BOOL Line_CTS;
  BOOL Line_DSR;
  BOOL Line_RING;

  BOOL operator==(TSerialLines sl)
  {
    return ( (Line_DCD == sl.Line_DCD) &&
             (Line_CTS == sl.Line_CTS) &&
             (Line_DSR == sl.Line_DSR) &&
             (Line_RING == sl.Line_RING) );
  };
  BOOL operator!=(TSerialLines sl)
  {
    return ( (Line_DCD != sl.Line_DCD) ||
             (Line_CTS != sl.Line_CTS) ||
             (Line_DSR != sl.Line_DSR) ||
             (Line_RING != sl.Line_RING) );
  };
};

typedef boost::signals2::connection _signal_conn_type;
   //Define a signal connection

typedef boost::signals2::signal<void ()> _signal_void_type;
   //Define a signal type for a void f() event
typedef boost::signals2::signal<void (INT)> _signal_error_type;
   //Define a signal type for a void f(INT) event
typedef boost::signals2::signal<void (INT, BOOL)>
_signal_int_bool_type; //Define a signal type for a void f(INT, BOOL) event
typedef boost::signals2::signal<void (std::string)> _signal_string_type;
  //Define a signal type for a void f(std::string) event

typedef _signal_void_type::slot_type _slot_type_void;
//Define a slot type for connecting to the signal void
typedef _signal_error_type::slot_type _slot_type_error;
//Define a slot type for connecting to the signal error
typedef _signal_int_bool_type::slot_type _slot_type_int_bool;
//Define a slot type for connecting to the signal (int, bool)
typedef _signal_string_type::slot_type _slot_type_string;
//Define a slot type for connecting to the signal (std::string)

//typedef std::vector<CHAR> TBUFFER;
typedef std::string TBUFFER;

/*!
  \class SerialPort
 */
class SerialPort : public boost::enable_shared_from_this<SerialPort>
{

public:
  /*************************** PROPERTIES *****************************/

  /*!
    \brief Return the selected COM port number.
    \return COM port selected.
   */
  CHAR GetComPortNo(void);
  /*!
    \brief Return the selected COM port name.
    \return COM port selected.
   */
  std::string GetComPortStr(void);

  /*!
    \brief Set the COM port device.
    \param ComNo: New COM port number
   */
  void SetComPort(CHAR ComNo);
  /*!
    \brief Set the COM port device.
    \param DevName: New COM port name (eg. "\\\\.\\COM1")
   */
  void SetComPort(std::string DevName);

  /*!
    \brief Get the size of RX buffer.
    \return The current RX buffer size.
   */
  INT GetBufferSize(void);
  /*!
    \brief Set the size of RX buffer.
    \param value: The new RX buffer size.
   */
  void SetBufferSize(INT value);

  /*!
    \brief Get the Device Control Structure.
    \param DCS: A pointer to the returned struct.
   */
  void GetSerialDCS(SerialDCS *DCS);
  /*!
    \brief Set a new Device Control Structure.
    To take the edit be applied, close and re-open the device.
    \param DCS: A pointer to the new DCS
   */
  void SetSerialDCS(SerialDCS *DCS);

  /*!
    \brief Set the new state of the RS232 DTR Pin.
    \param value: New state of DTR: False -> off, True -> On.
   */
  void SetDTR(BOOL value);

  /*!
    \brief Set the new state of the RS232 RTS Pin.
    \param value: New state of RTS: False -> off, True -> On.
   */
  void SetRTS(BOOL value);

  /*************************** METHODS *****************************/

  /*!
    \brief Constructors.
   */
  SerialPort(boost::asio::io_service &_io);
  SerialPort(boost::asio::io_service &_io, INT BufferSize);

  /*!
    \brief Destructor.
   */
  ~SerialPort();

  /*!
    \brief Write the buffer on the Serial device.
    \param buffer: Pointer to the buffer to be wrote.
    \return True on success, False otherwise.
   */
  BOOL Write(TBUFFER buffer);

  /*!
    \brief Read the incoming buffer.
    \param Pointer to a vector where the buffer must be copied into.
   */
  void GetData(TBUFFER &buffer);
  //TBUFFER GetData(void);

  /*!
    \brief Open the device.
    \return True on success, False otherwise.
   */
  BOOL Open(void);

  /*!
    \brief Open the device.
    \param ComNo: Optional new device number.
    \return True on success, False otherwise.
   */
  BOOL Open(CHAR ComNo);

  /*!
    \brief Open the device.
    \param DevName: Optional DevName new device name (eg. "\\\\.\\COM1").
    \return True on success, False otherwise.
   */
  BOOL Open(std::string DevName);

  /*!
    \brief Close the device.
    \return True on success, False otherwise.
   */
  BOOL Close(void);

  /*!
    \brief Flushes the device.
   */
  void Flush(void);

  /*!
    \brief Connection point to a call-back that receive OnData event.
           This event is raised when some data is readed from the device
           and is ready to be catched by GetData member.
    \param A reference to a connection Object.
    \return The new connection object created.
   */
  _signal_conn_type OnData(const _slot_type_void& s);

  /*!
    \brief Connection point to a call-back that receive OnLineState event.
           This event is raised when one of the RS232 lines changes its
state.
    \param A reference to a connection Object.
   */
  _signal_conn_type OnLineState(const _slot_type_int_bool& s);

  /*!
    \brief Connection point to a call-back that receive OnLineState event.
           This event is raised when one of the RS232 lines changes its
state.
    \param A reference to a connection Object.
   */
  _signal_conn_type OnError(const _slot_type_error& s);

  /*!
    \brief Return the io_service connected to the serial_port object
   */
  boost::asio::io_service & get_io_service();

private:

  void read_complete(const boost::system::error_code& error, size_t
bytes_transferred);
  void do_write(TBUFFER &buffer);
  //void do_close(const boost::system::error_code& error);
  void Post_OnData();
  void timer_exipred(const boost::system::error_code& error);
  void read_start(void); // reading from serial port

  void CommEventThreadFunc();

public:

  static const char CTRL_Z = 26;

  bool UseEosString;
  std::string EosString;
  UINT ReadTimeOut;

private:

  SerialDCS _dcs;

  io_service& _io_service; // the main IO service that runs this
connection
  serial_port _serialPort; // the serial port this instance is
connected to
  deadline_timer _rx_timer;
  boost::mutex _mut;

  boost::scoped_ptr<boost::thread> _CommEventThread;
  bool _CommThreadRunning;

  size_t m_buffer_size;
  std::string m_ComPort;
  TBUFFER _read_buffer;
  PCHAR _buf;
  bool _closing;

  _signal_void_type signal_OnData; //Raised when data is readed
  _signal_int_bool_type signal_OnLineState; //Raised when a line has
changed its state
  _signal_error_type signal_OnError; //Raised when an error
occurred

  std::vector<_signal_conn_type> _sig_connections;

};

#endif //_SERIAL_H

/////////////////////////////////////////////////////////////////////////////
// Name: Serial.cpp
// Purpose:
// Author: Daniele Barzotti
// Id: $Id: Serial.cpp,v 1.0.0.0 2009/02/-- jb Exp $
// Copyright: (c) 2009 Eurocom Telecomunicazioni
// Licence:
/////////////////////////////////////////////////////////////////////////////

#include "Serial.hpp"
#include <cstring>
#include <vector>
#include <algorithm>

using namespace boost::asio;

// Map to associate the strings with the enum values
typedef std::vector<std::string> str_vector;

static str_vector s_DevNames;

static void InitDeviceNames(void)
{
  static BOOL init = false;

  if (init) return;
  init = true;

  s_DevNames.reserve(20);
  s_DevNames.push_back("0"); //Dummy value: not valid!
  s_DevNames.push_back(wxCOM1);
  s_DevNames.push_back(wxCOM2);
  s_DevNames.push_back(wxCOM3);
  s_DevNames.push_back(wxCOM4);
  s_DevNames.push_back(wxCOM5);
  s_DevNames.push_back(wxCOM6);
  s_DevNames.push_back(wxCOM7);
  s_DevNames.push_back(wxCOM8);
  s_DevNames.push_back(wxCOM9);
  s_DevNames.push_back(wxCOM10);
  s_DevNames.push_back(wxCOM11);
  s_DevNames.push_back(wxCOM12);
  s_DevNames.push_back(wxCOM13);
  s_DevNames.push_back(wxCOM14);
  s_DevNames.push_back(wxCOM15);
  s_DevNames.push_back(wxCOM16);

};

//--------------------------------------------------------------------------------------

void SerialPort::GetData(TBUFFER& buffer)
{
  // Lock the read buffer vector
  boost::mutex::scoped_lock lock(_mut);
  buffer.assign(_read_buffer.begin(), _read_buffer.end());
  //Clear the read buffer
  _read_buffer.clear();
};

//--------------------------------------------------------------------------------------

void SerialPort::Post_OnData()
{
  // Raise the event
  if (!signal_OnData.empty()) signal_OnData();
};

//--------------------------------------------------------------------------------------

void SerialPort::timer_exipred(const boost::system::error_code& error)
{
  if (!error)
  {
    #if (WIN32 && _DEBUG)
      std::string sd("SerialPort::timer_exipred = ");
      sd += _read_buffer;
      OutputDebugStringA((LPCSTR)sd.c_str());
    #endif

    // Timer expired-> read completed, so process the data
    _io_service.post( boost::bind(&SerialPort::Post_OnData, this) );
  }
}

//--------------------------------------------------------------------------------------

void SerialPort::read_complete(const boost::system::error_code& error,
size_t bytes_transferred)
{

  if (_closing) return;

  // the asynchronous read operation has now completed or failed and
returned an error
  if (!error)
  {
    // saving data to vector
    if (true)
    {
      boost::mutex::scoped_lock lock(_mut);
      _read_buffer.append(_buf, bytes_transferred);
    }

    OutputDebugStringA("SerialPort::read_cpmplete -> StartTimer\n");

    // Start a new timer or renew it
    _rx_timer.expires_from_now(
boost::posix_time::milliseconds(ReadTimeOut) );
    // We managed to cancel the timer. Start new asynchronous wait.
    _rx_timer.async_wait( boost::bind(&SerialPort::timer_exipred, this,
_1) );
  }
  else
    if (!signal_OnError.empty()) signal_OnError(error.value());

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

};

//--------------------------------------------------------------------------------------

void SerialPort::read_start()
{
  if (_closing) return;

  OutputDebugStringA("SerialPort::read_start\n");

  // Start an asynchronous read and call read_complete when it completes
or fails
  _serialPort.async_read_some(boost::asio::buffer(_buf, m_buffer_size),
                              boost::bind(&SerialPort::read_complete,
                              this,
                              boost::asio::placeholders::error,

boost::asio::placeholders::bytes_transferred));
};

//--------------------------------------------------------------------------------------

void SerialPort::SetDTR(BOOL value)
{
  //Toggle the RTS pin
  #ifdef WIN32
    EscapeCommFunction(_serialPort.native(), (value ? SETDTR : CLRDTR));
  #else
    // Others implementations
  #endif
};

//--------------------------------------------------------------------------------------

void SerialPort::SetRTS(BOOL value)
{
  //Toggle the RTS pin
  #ifdef WIN32
    EscapeCommFunction(_serialPort.native(), (value ? SETRTS : CLRRTS));
  #else
    // Others implementations
  #endif
};

//--------------------------------------------------------------------------------------

void SerialPort::SetBufferSize(INT value)
{
  m_buffer_size = value;

  if (_buf) delete[] _buf;
  _buf = new char[m_buffer_size];
  memset(_buf, 0, m_buffer_size);
};

//--------------------------------------------------------------------------------------

INT SerialPort::GetBufferSize(){
  return m_buffer_size;
};

//--------------------------------------------------------------------------------------

boost::asio::io_service& SerialPort::get_io_service()
{
  return _io_service;
}

//--------------------------------------------------------------------------------------

SerialPort::SerialPort(boost::asio::io_service &_io, INT BufferSize) :
                       m_buffer_size(BufferSize),
                       _io_service(_io),
                       _serialPort(_io_service),
                       _rx_timer(_io_service),
                       ReadTimeOut(READ_TIMEOUT)
{
  EosString = '\n';
  _buf = 0;
};

//--------------------------------------------------------------------------------------

SerialPort::SerialPort(boost::asio::io_service &_io) :
                       m_buffer_size(512),
                       _io_service(_io),
                       _serialPort(_io_service),
                       _rx_timer(_io_service),
                       ReadTimeOut(READ_TIMEOUT)
{
  EosString = '\n';
  _buf = 0;
};

//--------------------------------------------------------------------------------------

SerialPort::~SerialPort()
{
  std::vector<_signal_conn_type>::iterator it;

  for(it = _sig_connections.begin(); it != _sig_connections.end(); it++)
    (*it).disconnect();

  _sig_connections.clear();

  this->Close();

  if (_buf) delete[] _buf;

};

//--------------------------------------------------------------------------------------

CHAR SerialPort::GetComPortNo(void)
{
  str_vector::const_iterator it;
  it = std::find(s_DevNames.begin(), s_DevNames.end(), m_ComPort);
  return (it - s_DevNames.begin());
};

//--------------------------------------------------------------------------------------

std::string SerialPort::GetComPortStr(void)
{
  return m_ComPort;
};
//--------------------------------------------------------------------------------------

void SerialPort::SetComPort(CHAR ComNo)
{
  if (ComNo > 0) m_ComPort = s_DevNames[ComNo];
};

//--------------------------------------------------------------------------------------

void SerialPort::SetComPort(std::string DevName)
{
  m_ComPort = DevName;
};
//--------------------------------------------------------------------------------------

void SerialPort::GetSerialDCS(SerialDCS *DCS)
{
  memcpy(DCS, &_dcs, sizeof(SerialDCS));
};

//--------------------------------------------------------------------------------------

void SerialPort::SetSerialDCS(SerialDCS *DCS)
{
  memcpy(&_dcs, DCS, sizeof(SerialDCS));

  try {
    if (_serialPort.is_open())
    {
      serial_port_base::baud_rate baud_option(_dcs.baud);
      _serialPort.set_option( baud_option );

      serial_port_base::flow_control
flow_option((serial_port_base::flow_control::type)_dcs.flowcontrol);
      _serialPort.set_option( flow_option );

      serial_port_base::parity
parity_option((serial_port_base::parity::type)_dcs.parity);
      _serialPort.set_option( parity_option );

      serial_port_base::stop_bits
stopbits_option(serial_port_base::stop_bits::type::one);
      _serialPort.set_option( stopbits_option );
    }
  }
  catch (...)
  {
    //cerr << "Exception: " << e.what() << "\n";
  }
};

//--------------------------------------------------------------------------------------

BOOL SerialPort::Write(TBUFFER buffer)
{
  size_t byte_written = boost::asio::write(_serialPort,
boost::asio::buffer(buffer.c_str(), buffer.size()));
  return (byte_written == buffer.size());
};

//--------------------------------------------------------------------------------------

BOOL SerialPort::Open(void)
{
  boost::system::error_code ec;
  ec = _serialPort.open(m_ComPort, ec);
  if (!_serialPort.is_open()) return false;

  SetSerialDCS(&_dcs);
  SetBufferSize(m_buffer_size);

  _closing = false;

  _CommEventThread.reset(new boost::thread(
(boost::bind(&SerialPort::CommEventThreadFunc, this)) ));

  read_start();
  return true;
};

//--------------------------------------------------------------------------------------

BOOL SerialPort::Open(std::string DevName)
{
  if (DevName.length() > 0) SetComPort(DevName);
  return Open();
};

//--------------------------------------------------------------------------------------

BOOL SerialPort::Open(CHAR ComNo)
{
  if (ComNo > 0) SetComPort(ComNo);
  return Open();
};

//--------------------------------------------------------------------------------------

void SerialPort::Flush(void)
{
#ifdef WIN32
  FlushFileBuffers(_serialPort.native());
#else
  // Others implementations
#endif
  _read_buffer.clear();
};

//--------------------------------------------------------------------------------------

BOOL SerialPort::Close(void)
{
  if (_serialPort.is_open())
  {
    _CommThreadRunning = FALSE;

    _closing = true;
    _rx_timer.cancel();
    _serialPort.close();

    _CommEventThread->join();
  }
  return true;
};

//--------------------------------------------------------------------------------------

_signal_conn_type SerialPort::OnData(const _slot_type_void& s)
{
  _sig_connections.push_back( signal_OnData.connect(s) );
  return _sig_connections.back();
};

//--------------------------------------------------------------------------------------

_signal_conn_type SerialPort::OnLineState(const _slot_type_int_bool& s)
{
  _sig_connections.push_back( signal_OnLineState.connect(s) );
  return _sig_connections.back();
};

//--------------------------------------------------------------------------------------

void SerialPort::CommEventThreadFunc()
{
  DWORD dwCommStatus;
  DWORD dwLineStat;
  SerialLinesEnum line;
  BOOL line_state = FALSE;

  _CommThreadRunning = TRUE;

  while(this->_CommThreadRunning)
  {
    SetCommMask(_serialPort.native(), EV_CTS | EV_DSR | EV_RING | EV_RLSD);
    WaitCommEvent(_serialPort.native(), &dwCommStatus, 0);

    if (!signal_OnLineState.empty()) {

      GetCommModemStatus(_serialPort.native(), &dwLineStat);

      #if(WIN32 && _WIN32_DCOM)
        HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
      #endif

      if (dwCommStatus & EV_CTS)
        signal_OnLineState(SERIAL_LINESTATE_CTS, (dwLineStat &
MS_CTS_ON));

      if (dwCommStatus & EV_DSR)
        signal_OnLineState(SERIAL_LINESTATE_DSR, (dwLineStat & MS_DSR_ON));

      if (dwCommStatus & EV_RING)
        signal_OnLineState(SERIAL_LINESTATE_RING, (dwLineStat &
MS_RING_ON));

      if (dwCommStatus & EV_RLSD)
        signal_OnLineState(SERIAL_LINESTATE_DCD, (dwLineStat &
MS_RLSD_ON));

      #if(WIN32 && _WIN32_DCOM)
        CoUninitialize();
      #endif

    }
  }
}

  * Sconosciuta - rilevata
  * Inglese
  * Italiano

  * Inglese
  * Italiano

 <javascript:void(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