mt4cpp
SerialPort.hpp
Go to the documentation of this file.
1 /**
2  * \file SerialPort.hpp
3  * \brief The serial port with timeout based on Boost.Asio.
4  */
5 
6 
7 #ifndef MT4CPP_SERIAL_PORT_HPP
8 #define MT4CPP_SERIAL_PORT_HPP
9 
10 #include <vector>
11 #include <string>
12 
13 #include <boost/bind.hpp>
14 #include <boost/function.hpp>
15 #include <boost/asio.hpp>
16 #include <boost/asio/basic_waitable_timer.hpp>
17 
18 #include <boost/chrono.hpp>
19 
20 namespace mt4cpp {
21 
22  /**
23  \brief the template for object represents serial port with timeout
24 
25  \param clock_type the type of the clock, e.g. boost::chrono::steady_clock
26  */
27  template<typename clock_type>
28  class BasicSerialPort : boost::noncopyable {
29  public:
30  typedef std::vector<char> Buffer; //buffer for read or write
31  typedef clock_type Clock; //the clock used for timeout
32  typedef typename Clock::duration Timeout; //representation of timeout
33  typedef boost::function1<bool, const Buffer&> ReadPredicate; //representation of predicate
34 
35  /** c-tor */
36  BasicSerialPort(const std::string& port_name,
37  const Timeout timeout,
38  boost::asio::serial_port_base::baud_rate baud_rate
39  = boost::asio::serial_port_base::baud_rate(9600),
40  boost::asio::serial_port_base::flow_control flow_control
41  = boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none),
42  boost::asio::serial_port_base::parity parity
43  = boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none),
44  boost::asio::serial_port_base::stop_bits stop_bits
45  = boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one),
46  boost::asio::serial_port_base::character_size character_size
47  = boost::asio::serial_port_base::character_size(8) )
48  :
49  timeout_(timeout),
50  io_(),
51  port_(io_, port_name ),
52  timer_(io_)
53  {
54  port_.set_option(baud_rate);
55  port_.set_option(flow_control);
56  port_.set_option(parity);
57  port_.set_option(stop_bits);
58  port_.set_option(character_size);
59  }
60 
61  /** d-tor */
63  port_.close();
64  }
65 
66  /** read from port when predicate return true_type */
67  inline const Buffer& read_if(ReadPredicate p) {
68  readBuffer_.clear();
69  if( !p(readBuffer_) )
70  return readBuffer_; //if reading is finished for empty buffer
71 
72  timer_.expires_from_now( timeout_ );
73  timer_.async_wait(boost::bind(&BasicSerialPort::timerEvent, this, boost::asio::placeholders::error) );
74  readSome(p);
75  io_.reset();
76  io_.run();
77  return readBuffer_;
78  }
79 
80  /** read n bytes */
81  inline const Buffer& read_n(int n) {
82  return read_if( boost::bind( std::less<int>(), boost::bind(&Buffer::size, _1), n ) );
83  }
84 
85  /** write into port. Return number bytes written. */
86  inline Buffer::size_type write(const Buffer& buf) { //!< write buffer to port. Return number of written bytes
87  writeBuffer_ = buf;
88  bytesWritten_ = 0;
89  cancel_ = false;
90 
91  if(writeBuffer_.empty() )
92  return 0; //if try to write empty buffer
93 
94  timer_.expires_from_now( timeout_ );
95  timer_.async_wait(boost::bind(&BasicSerialPort::timerEvent, this, boost::asio::placeholders::error) );
96  writeSome();
97  io_.reset();
98  io_.run();
99  return bytesWritten_;
100  }
101  private:
102  Timeout timeout_;
103  boost::asio::io_service io_;
104  boost::asio::serial_port port_; //boost serial port
105  boost::asio::basic_waitable_timer<Clock> timer_; //timer to timeout
106 
107  static const Buffer::size_type BUFFER_SIZE = 16;
108  char buffer_ [BUFFER_SIZE]; // transmission buffer
109  Buffer readBuffer_; //whole message buffer for read
110  Buffer writeBuffer_; //whole message buffer for write
111  Buffer::size_type bytesWritten_; //number written bytes
112  bool cancel_; //cancel the write operation
113  private:
114  /** start async reading from port using boost asio */
115  inline void readSome(ReadPredicate p) {
116  port_.async_read_some( boost::asio::buffer(buffer_, 1),
117  boost::bind(&BasicSerialPort::readEvent, this,
118  boost::asio::placeholders::error,
119  boost::asio::placeholders::bytes_transferred,
120  p) );
121 
122  }
123 
124  /** start async writting to port using boost asio */
125  inline void writeSome() {
126  Buffer::size_type to_write = std::min(writeBuffer_.size() - bytesWritten_, BUFFER_SIZE);
127  Buffer::const_iterator b = writeBuffer_.begin() + bytesWritten_;
128  Buffer::const_iterator e = b + to_write;
129  std::copy( b, e, buffer_ );
130 
131  port_.async_write_some( boost::asio::buffer(buffer_, to_write ),
132  boost::bind(&BasicSerialPort::writeEvent, this,
133  boost::asio::placeholders::error,
134  boost::asio::placeholders::bytes_transferred) );
135 
136  }
137  /** callback called by async_read_some on given port */
138  inline void readEvent(const boost::system::error_code& error, std::size_t bytes_transferred, ReadPredicate p) {
139  if(!error) { //not ERROR and not TIMEOUT
140  if(bytes_transferred > 0) { //read some data
141  readBuffer_.push_back(buffer_[0]);
142  if( ! p(readBuffer_) ) { //predicate not true -> finish reading
143  timer_.cancel(); // will cause timer to fire with an error
144  return;
145  }
146  }
147  readSome(p);
148  }
149  }
150 
151  inline Buffer::size_type initWriteBuffer() {
152  Buffer::size_type to_write = std::min(writeBuffer_.size() - bytesWritten_, BUFFER_SIZE);
153  Buffer::const_iterator b = writeBuffer_.begin() + bytesWritten_;
154  Buffer::const_iterator e = b + to_write;
155  std::copy( b, e, buffer_ );
156  return to_write;
157  }
158 
159  /** callback called by async_write_some on given port */
160  inline void writeEvent(const boost::system::error_code& error, std::size_t bytes_transferred) {
161  //std::cout << "write event, bw_:" << bytesWritten_ << " e: " << error << " tr: " << bytes_transferred << " c:" << cancel_ << std::endl;
162  if(!error && !cancel_) { //not ERROR and not TIMEOUT
163  if(bytes_transferred > 0) { //write some data
164  bytesWritten_ += bytes_transferred;
165  if(bytesWritten_ >= writeBuffer_.size()) {
166  timer_.cancel(); // will cause timer to fire with an error
167  return;
168  }
169  }
170  writeSome();
171  }
172  }
173 
174  /** timer event - timeout */
175  inline void timerEvent(const boost::system::error_code& error) {
176  if( error ) // Data was read and this timeout was canceled
177  return;
178  port_.cancel(); // will cause readEvent fire with an error
179  cancel_ = true;
180  }
181  };
182 
183  /** Typedef for serial port with a timer based on the Boost.Chrono steady clock */
184  typedef BasicSerialPort<boost::chrono::steady_clock> SerialPort;
185 
186 } //namespace mt4cpp
187 
188 #endif // MT4CPP_SERIAL_PORT_HPP
~BasicSerialPort()
Definition: SerialPort.hpp:62
Definition: Command.hpp:21
the template for object represents serial port with timeout
Definition: SerialPort.hpp:28
const Buffer & read_if(ReadPredicate p)
Definition: SerialPort.hpp:67
BasicSerialPort(const std::string &port_name, const Timeout timeout, boost::asio::serial_port_base::baud_rate baud_rate=boost::asio::serial_port_base::baud_rate(9600), boost::asio::serial_port_base::flow_control flow_control=boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none), boost::asio::serial_port_base::parity parity=boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none), boost::asio::serial_port_base::stop_bits stop_bits=boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one), boost::asio::serial_port_base::character_size character_size=boost::asio::serial_port_base::character_size(8))
Definition: SerialPort.hpp:36
Buffer::size_type write(const Buffer &buf)
Definition: SerialPort.hpp:86
const Buffer & read_n(int n)
Definition: SerialPort.hpp:81