[ASIO] Synchronous write on serial_port
Hi, I'm developing a library that use ASIO Serial Port and I need to implement a synchronous write. The structure is: EuroATLib.cpp : library main file Serial.cpp : serial port class that wrap asio::serial_port ATDispatcher.cpp : a dispatcher object ATModem.cpp : the consumer object The problem is that my code writes characters on serial port very slowly (1 char every 10 seconds) and I can't understand why. Also, when the program ends, the io_service thread seems to be already alive: it continues to write to serial port for a while! The implementation is like this...can someone give me some feedback? PS: In the following code TBUFFER is std:string. In EuroATLib.cpp : ---------------------------------------------------------- // Main io_service static boost::asio::io_service io_service; static boost::scoped_ptrboost::thread io_service_thread; /*! \brief Factory function for ATModem objects creation */ EXTERN_C EUROATLIB_API ATModem* CALL CreateATModemObj() { static bool thread_started; if (!thread_started) { try { // run the IO service as a separate thread io_service_thread.reset(new boost::thread( boost::bind(&boost::asio::io_service::run, &io_service) )); thread_started = !thread_started; } catch (boost::thread_resource_error e) { // Failed to create the new thread return 0; } } // create the new object return new ATModem(io_service); }; ---------------------------------------------------------- In Serial.hpp : ---------------------------------------------------------- // the main IO service that runs this connection boost::asio::io_service& _io_service; // the serial port this instance is connected to boost::asio::serial_port _serialPort; ---------------------------------------------------------- In Serial.cpp : ---------------------------------------------------------- SerialPort::SerialPort(boost::asio::io_service &_io) : m_buffer_size(100), _io_service(_io), _serialPort(_io_service), _rx_timer(_io_service), ReadTimeOut(READ_TIMEOUT) { //init_dcs(); EosString = '\n'; _buf = 0; }; BOOL SerialPort::Write(TBUFFER &buffer) { _io_service.post(boost::bind(&SerialPort::do_write, this, buffer)); return true; }; void SerialPort::do_write(TBUFFER &buffer) { // size_t byte_written = boost::asio::write(_serialPort, boost::asio::buffer(buffer.c_str(), buffer.size())); size_t byte_written = _serialPort.write_some(boost::asio::buffer(buffer.c_str(), buffer.size())); }; ---------------------------------------------------------- In ATDispatcher.cpp simply have a SerialPort member and a function SendCommand that start a timer and call Serial.Write(cmd) In ATModem.cpp I have an ATDispatcher Object on which it calls disp_obj.SendCommand("AT+CMGS"): ---------------------------------------------------------- RET_VAL ATModem::Connect(std::string DevName) { if (DevName.length() > 0) SetComPort(DevName); if (!_serial_port.Open()) return -1; //Connect the OnDataReceived event to the ATDispatcher object _dispatcher->OnData(boost::bind(&EuroATLib::ATModem::OnDataReceived, this)); //Register Voice Service if ( _dispatcher->SendCommand("AT+CTSP=2,0,0") != ATDispatcher::RET_OK ) return DEV_AT_CMD_ERROR; //Register STATUS Service if ( _dispatcher->SendCommand("AT+CTSP=2,2,20") != ATDispatcher::RET_OK ) return DEV_AT_CMD_ERROR; this->SetStatus(STATE_READY); }; ---------------------------------------------------------- Finally in the main client program: // Create a ATModem object boost::scoped_ptr<ATModem> _devATModem( CreateATModemObj() ); _devATModem->Connect("\\\\.\\COM1");
Hi, I've isolated the problem (I think). But I can't understand why it works in this manner!! I've posted a working code in attach (made with VS2008 EXP), if someone could give me some feedback I will grate to him! In the code there are two wrapper classes for the asio::serial_port, one that works well and the other (mine) that has the issue! Thanks in advance. Daniele. Daniele Barzotti wrote:
Hi,
I'm developing a library that use ASIO Serial Port and I need to implement a synchronous write.
The structure is:
EuroATLib.cpp : library main file Serial.cpp : serial port class that wrap asio::serial_port ATDispatcher.cpp : a dispatcher object ATModem.cpp : the consumer object
The problem is that my code writes characters on serial port very slowly (1 char every 10 seconds) and I can't understand why. Also, when the program ends, the io_service thread seems to be already alive: it continues to write to serial port for a while!
The implementation is like this...can someone give me some feedback?
PS: In the following code TBUFFER is std:string.
In EuroATLib.cpp : ---------------------------------------------------------- // Main io_service static boost::asio::io_service io_service; static boost::scoped_ptrboost::thread io_service_thread;
/*! \brief Factory function for ATModem objects creation */ EXTERN_C EUROATLIB_API ATModem* CALL CreateATModemObj() { static bool thread_started; if (!thread_started) { try { // run the IO service as a separate thread io_service_thread.reset(new boost::thread( boost::bind(&boost::asio::io_service::run, &io_service) )); thread_started = !thread_started; } catch (boost::thread_resource_error e) { // Failed to create the new thread return 0; } } // create the new object return new ATModem(io_service); }; ----------------------------------------------------------
In Serial.hpp : ---------------------------------------------------------- // the main IO service that runs this connection boost::asio::io_service& _io_service; // the serial port this instance is connected to boost::asio::serial_port _serialPort; ----------------------------------------------------------
In Serial.cpp : ----------------------------------------------------------
SerialPort::SerialPort(boost::asio::io_service &_io) : m_buffer_size(100), _io_service(_io), _serialPort(_io_service), _rx_timer(_io_service), ReadTimeOut(READ_TIMEOUT) { //init_dcs(); EosString = '\n'; _buf = 0; };
BOOL SerialPort::Write(TBUFFER &buffer) { _io_service.post(boost::bind(&SerialPort::do_write, this, buffer)); return true; };
void SerialPort::do_write(TBUFFER &buffer) { // size_t byte_written = boost::asio::write(_serialPort, boost::asio::buffer(buffer.c_str(), buffer.size())); size_t byte_written = _serialPort.write_some(boost::asio::buffer(buffer.c_str(), buffer.size())); };
----------------------------------------------------------
In ATDispatcher.cpp simply have a SerialPort member and a function SendCommand that start a timer and call Serial.Write(cmd)
In ATModem.cpp I have an ATDispatcher Object on which it calls disp_obj.SendCommand("AT+CMGS"):
---------------------------------------------------------- RET_VAL ATModem::Connect(std::string DevName) { if (DevName.length() > 0) SetComPort(DevName); if (!_serial_port.Open()) return -1;
//Connect the OnDataReceived event to the ATDispatcher object _dispatcher->OnData(boost::bind(&EuroATLib::ATModem::OnDataReceived, this));
//Register Voice Service if ( _dispatcher->SendCommand("AT+CTSP=2,0,0") != ATDispatcher::RET_OK ) return DEV_AT_CMD_ERROR; //Register STATUS Service if ( _dispatcher->SendCommand("AT+CTSP=2,2,20") != ATDispatcher::RET_OK ) return DEV_AT_CMD_ERROR;
this->SetStatus(STATE_READY); }; ----------------------------------------------------------
Finally in the main client program:
// Create a ATModem object boost::scoped_ptr<ATModem> _devATModem( CreateATModemObj() ); _devATModem->Connect("\\\\.\\COM1");
I've isolated the problem (I think). But I can't understand why it works in this manner!!
I've posted a working code in attach (made with VS2008 EXP), if someone could give me some feedback I will grate to him!
In the code there are two wrapper classes for the asio::serial_port, one that works well and the other (mine) that has the issue!
Your code doesn't compile with _USE_SERIAL_OK defined. But anyway, could you elaborate a bit more on what exactly you isolated and in what manner it works now. If you mean slow write to COM, then - did you try to do the same operation using some native WinAPI? Does it work ok?
Igor R ha scritto:
Your code doesn't compile with _USE_SERIAL_OK defined. But anyway, could you elaborate a bit more on what exactly you isolated and in what manner it works now. If you mean slow write to COM, then - did you try to do the same operation using some native WinAPI? Does it work ok?
Hi Igor, first thanks for your reply. I wrong to put a file the the zip. The one in attach should compile also with _USE_SERIAL_OK. But anyway...using that directive, the code use the mini_com client example found on the net and, of course, it works. So starting from this, I've substituted it with my Serial class and the write on COM is extremely slow. When I've said I've isolated the problem I would say that the issue is in my serial class wrapper (Serial.cpp) and not in the others classes. Have you try to use my code without _USE_SERIAL_OK? Did you have the same issue? PS. It is an example so by default it open the device "CNCA0" in ATModem::Connect if (!_serial_port.Open("CNCA0")) return; Tnx again, Daniele.
Daniele Barzotti wrote:
Hi Igor, first thanks for your reply. I wrong to put a file the the zip. The one in attach should compile also with _USE_SERIAL_OK. But anyway...using that directive, the code use the mini_com client example found on the net and, of course, it works. So starting from this, I've substituted it with my Serial class and the write on COM is extremely slow. When I've said I've isolated the problem I would say that the issue is in my serial class wrapper (Serial.cpp) and not in the others classes.
Ok, I've found the problem. In SerialPort::Open() I've deleted the line: SetSerialDCS(&_dcs); which is : ------------------------------------------------------------- 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::baud_rate flow_option(_dcs.flowcontrol); _serialPort.set_option( flow_option ); serial_port_base::baud_rate parity_option(_dcs.parity); _serialPort.set_option( parity_option ); serial_port_base::baud_rate stopbits_option(_dcs.stopbits); _serialPort.set_option( stopbits_option ); } } catch (...) {} }; ------------------------------------------------------------- And now the write works ok. But I've another issue on reading... My reading procedure is: SerialPort::read_start | | SerialPort::read_complete | | (Start an rx timer) | | SerialPort::Timer_Expired | | Raise the 'event' DataReveived Ok, if I put a breakpoint on one of these levels, all works and I see the data received, if I run the code without breakpoints the SerialPort::read_complete is not called. Follow the code... //------------------------------------------------------------ void SerialPort::Post_OnData() { // Raise the event std::cout << "SerialPort::Post_OnData" << std::endl; signal_OnData(); }; //------------------------------------------------------------ void SerialPort::timer_exipred(const boost::system::error_code& error) { if (!error) { std::cout << "SerialPort::timer_exipred" << std::endl; // 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) { // the asynchronous read operation has now completed or failed and returned an error if (!error) { std::cout << "SerialPort::read_complete" << std::endl; // saving data to vector _read_buffer.append(_buf, bytes_transferred); //Start a new timer //_rx_timer.expires_from_now( boost::posix_time::milliseconds(ReadTimeOut) ); _rx_timer.expires_from_now( boost::posix_time::milliseconds(50) ); // Start new asynchronous wait. _rx_timer.async_wait( boost::bind(&SerialPort::timer_exipred, this, _1) ); // start waiting for another asynchronous read again read_start(); } }; //------------------------------------------------------------ void SerialPort::read_start() { std::cout << "SerialPort::read_start" << std::endl; // 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)); }; //------------------------------------------------------------ Thanks, Daniele.
Daniele Barzotti ha scritto:
Daniele Barzotti wrote:
Ok, I've found the problem. .... But I've another issue on reading...
My reading procedure is:
SerialPort::read_start | | SerialPort::read_complete | | (Start an rx timer) | | SerialPort::Timer_Expired | | Raise the 'event' DataReveived
the SerialPort::read_complete is not called.
Ok, it's not called because the io_service thread was terminated! I've thought that while there was a call to async_read_some the thread was running...now I've used the io_service::work and everything work. Thanks.
participants (2)
-
Daniele Barzotti
-
Igor R