Port série tout ça : LA solution propre via BOOST::ASIO

Port série tout ça : LA solution propre via BOOST::ASIO - C++ - Programmation

Marsh Posté le 07-10-2008 à 08:47:39    

J'en avais un peu plein les fesses de ces 901085401654.464 topic sur le port série en C ou en C++.
Je reporte ici un exemple minimaliste proposé par Jeff Gray sur la mailing list boost
qui utilise ASIO pour faire ce genre de chose.
 
Il nécessite boost 1.36 ou ultérieur pour compiler.
 

Code :
  1. /* minicom.cpp
  2. A simple demonstration minicom client with Boost asio
  3.  
  4. Parameters:
  5.  baud rate
  6.  serial port (eg /dev/ttyS0 or COM1)
  7.  
  8. To end the application, send Ctrl-C on standard input
  9. */
  10. #include <deque>
  11. #include <iostream>
  12. #include <boost/bind.hpp>
  13. #include <boost/asio.hpp>
  14. #include <boost/asio/serial_port.hpp>
  15. #include <boost/thread.hpp>
  16. #include <boost/lexical_cast.hpp>
  17. #include <boost/date_time/posix_time/posix_time_types.hpp>
  18. #ifdef POSIX
  19. #include <termios.h>
  20. #endif
  21. using namespace std;
  22. class minicom_client
  23. {
  24. public:
  25. minicom_client(boost::asio::io_service& io_service, unsigned int baud, const string& device)
  26.  : active_(true),
  27.    io_service_(io_service),
  28.    serialPort(io_service, device)
  29. {
  30.  if (!serialPort.is_open())
  31.  {
  32.   cerr << "Failed to open serial port\n";
  33.   return;
  34.  }
  35.  boost::asio::serial_port_base::baud_rate baud_option(baud);
  36.  serialPort.set_option(baud_option); // set the baud rate after the port has been opened
  37.  read_start();
  38. }
  39. void write(const char msg) // pass the write data to the do_write function via the io service in the other thread
  40. {
  41.  io_service_.post(boost::bind(&minicom_client::do_write, this, msg));
  42. }
  43. void close() // call the do_close function via the io service in the other thread
  44. {
  45.  io_service_.post(boost::bind(&minicom_client::do_close, this, boost::system::error_code()));
  46. }
  47. bool active() // return true if the socket is still active
  48. {
  49.  return active_;
  50. }
  51. private:
  52. static const int max_read_length = 512; // maximum amount of data to read in one operation
  53. void read_start(void)
  54. { // Start an asynchronous read and call read_complete when it completes or fails
  55.  serialPort.async_read_some(boost::asio::buffer(read_msg_, max_read_length),
  56.   boost::bind(&minicom_client::read_complete,
  57.    this,
  58.    boost::asio::placeholders::error,
  59.    boost::asio::placeholders::bytes_transferred));
  60. }
  61. void read_complete(const boost::system::error_code& error, size_t bytes_transferred)
  62. { // the asynchronous read operation has now completed or failed and returned an error
  63.  if (!error)
  64.  { // read completed, so process the data
  65.   cout.write(read_msg_, bytes_transferred); // echo to standard output
  66.   read_start(); // start waiting for another asynchronous read again
  67.  }
  68.  else
  69.   do_close(error);
  70. }
  71. void do_write(const char msg)
  72. { // callback to handle write call from outside this class
  73.  bool write_in_progress = !write_msgs_.empty(); // is there anything currently being written?
  74.  write_msgs_.push_back(msg); // store in write buffer
  75.  if (!write_in_progress) // if nothing is currently being written, then start
  76.   write_start();
  77. }
  78. void write_start(void)
  79. { // Start an asynchronous write and call write_complete when it completes or fails
  80.  boost::asio::async_write(serialPort,
  81.   boost::asio::buffer(&write_msgs_.front(), 1),
  82.   boost::bind(&minicom_client::write_complete,
  83.    this,
  84.    boost::asio::placeholders::error));
  85. }
  86. void write_complete(const boost::system::error_code& error)
  87. { // the asynchronous read operation has now completed or failed and returned an error
  88.  if (!error)
  89.  { // write completed, so send next write data
  90.   write_msgs_.pop_front(); // remove the completed data
  91.   if (!write_msgs_.empty()) // if there is anthing left to be written
  92.    write_start(); // then start sending the next item in the buffer
  93.  }
  94.  else
  95.   do_close(error);
  96. }
  97. void do_close(const boost::system::error_code& error)
  98. { // something has gone wrong, so close the socket & make this object inactive
  99.  if (error == boost::asio::error::operation_aborted) // if this call is the result of a timer cancel()
  100.   return; // ignore it because the connection cancelled the timer
  101.  if (error)
  102.   cerr << "Error: " << error.message() << endl; // show the error message
  103.  else
  104.   cout << "Error: Connection did not succeed.\n";
  105.  cout << "Press Enter to exit\n";
  106.  serialPort.close();
  107.  active_ = false;
  108. }
  109. private:
  110. bool active_; // remains true while this object is still operating
  111. boost::asio::io_service& io_service_; // the main IO service that runs this connection
  112. boost::asio::serial_port serialPort; // the serial port this instance is connected to
  113. char read_msg_[max_read_length]; // data read from the socket
  114. deque<char> write_msgs_; // buffered write data
  115. };
  116. int main(int argc, char* argv[])
  117. {
  118. // on Unix POSIX based systems, turn off line buffering of input, so cin.get() returns after every keypress
  119. // On other systems, you'll need to look for an equivalent
  120. #ifdef POSIX
  121. termios stored_settings;
  122. tcgetattr(0, &stored_settings);
  123. termios new_settings = stored_settings;
  124. new_settings.c_lflag &= (~ICANON);
  125. new_settings.c_lflag &= (~ISIG); // don't automatically handle control-C
  126. tcsetattr(0, TCSANOW, &new_settings);
  127. #endif
  128. try
  129. {
  130.  if (argc != 3)
  131.  {
  132.   cerr << "Usage: minicom <baud> <device>\n";
  133.   return 1;
  134.  }
  135.  boost::asio::io_service io_service;
  136.  // define an instance of the main class of this program
  137.  minicom_client c(io_service, boost::lexical_cast<unsigned int>(argv[1]), argv[2]);
  138.  // run the IO service as a separate thread, so the main thread can block on standard input
  139.  boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
  140.  while (c.active()) // check the internal state of the connection to make sure it's still running
  141.  {
  142.   char ch;
  143.   cin.get(ch); // blocking wait for standard input
  144.   if (ch == 3) // ctrl-C to end program
  145.    break;
  146.   c.write(ch);
  147.  }
  148.  c.close(); // close the minicom client connection
  149.  t.join(); // wait for the IO service thread to close
  150. }
  151. catch (exception& e)
  152. {
  153.  cerr << "Exception: " << e.what() << "\n";
  154. }
  155. #ifdef POSIX // restore default buffering of standard input
  156. tcsetattr(0, TCSANOW, &stored_settings);
  157. #endif
  158. return 0;
  159. }


 
Le code devrait être assez claire au vue des noms des méthodes et des commentaires.

Reply

Marsh Posté le 07-10-2008 à 08:47:39   

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed