[asio::serial_port] I can read and write synchronously, but how do I unblock when I want to end?
I'm using boost::asio::serial_port to read and write to a serial port device. I do the reading inside a boost::thread to take advantage of RS-232 being full-duplex. My read calls are like this one: BYTE b; read( m_SerialPort, boost::asio::buffer(&b,1) ); Everything works OK except for shutdown. When I want to end the thread, it's almost always blocked in a read call. How do I unblock it? I tried: - Calling close on the port: This generates an exception, which I can catch, but I was told I can't distinguish shutdown from error if I do this. - Stopping the io_service I passed to the serial_port's constructor. This had no effect, the thread remained blocked on the read call. -- *Darío Eduardo Ramos* Meditech S.A. www.meditech.com.ar (+54) 01147603300, Interno 31 Av. Julio A. Roca 3456 Florida Oeste, Bs.As. Argentina
Il 02/09/2012 03:59 PM, Dario Ramos ha scritto:
I'm using boost::asio::serial_port to read and write to a serial port device. I do the reading inside a boost::thread to take advantage of RS-232 being full-duplex. My read calls are like this one:
BYTE b; read( m_SerialPort, boost::asio::buffer(&b,1) );
The best solution I'm aware of is to let boost handle the job and use async_read and async_write. -- Leo Cacciari Aliae nationes servitutem pati possunt populi romani est propria libertas
that's not true. asio will throw an "boost::asio::error::operation_aborted" error-code.
Ok, I tried this: try{ m_SerialPort.close(); }catch( const std::exception& ex ){ } But I got this error: Run-Time Check Failure #0 - The value of ESP was not properly saved across
a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.
Should I rebuild the boost libraries so that they use another convention? Which one? Our solution is a Visual Studio 2010 SP1 one, and has C# and VB.NET assemblies which talk to unmanaged C++ dlls through C++/CLI dlls. Very nice, huh? I never messed around with calling conventions, I'm currently investigating the subject so don't assume I know too much.
Run-Time Check Failure #0 - The value of ESP was not properly saved across
a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.
More info: I isolated the problem in a small unmanaged test console app which uses my class which uses boost::asio. I checked Properties->C/C++->Advanced->Calling Convention and it's set to __cdecl. If I try to set it to anything else, the project won't even build. So I'm guessing Boost is built with __cdecl. What can I do, then? Could this be a bug? 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? -- *Darío Eduardo Ramos* Meditech S.A. www.meditech.com.ar (+54) 01147603300, Interno 31 Av. Julio A. Roca 3456 Florida Oeste, Bs.As. Argentina
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);> <#>
participants (4)
-
Daniele Barzotti
-
Dario Ramos
-
Leo Cacciari
-
michi7x7