|
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