Async Comm
A library for asynchronous serial communication
comm.h
Go to the documentation of this file.
1 /*
2  * Software License Agreement (BSD-3 License)
3  *
4  * Copyright (c) 2018 Daniel Koch.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * * Redistributions of source code must retain the above copyright notice, this
11  * list of conditions and the following disclaimer.
12  *
13  * * Redistributions in binary form must reproduce the above copyright notice,
14  * this list of conditions and the following disclaimer in the documentation
15  * and/or other materials provided with the distribution.
16  *
17  * * Neither the name of the copyright holder nor the names of its
18  * contributors may be used to endorse or promote products derived from
19  * this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
38 #ifndef ASYNC_COMM_COMM_H
39 #define ASYNC_COMM_COMM_H
40 
41 #include <condition_variable>
42 #include <cstddef>
43 #include <cstdint>
44 #include <functional>
45 #include <list>
46 #include <mutex>
47 #include <thread>
48 
49 #include <boost/asio.hpp>
50 #include <boost/function.hpp>
51 
53 
54 namespace async_comm
55 {
56 
62 {
63 public:
74  virtual void receive_callback(const uint8_t * buf, size_t size) = 0;
75 };
76 
81 class Comm
82 {
83 public:
88  Comm(MessageHandler& message_handler = default_message_handler_);
89  virtual ~Comm();
90 
95  bool init();
96 
100  void close();
101 
107  void send_bytes(const uint8_t * src, size_t len);
108 
113  inline void send_byte(uint8_t data) { send_bytes(&data, 1); }
114 
128  void register_receive_callback(std::function<void(const uint8_t*, size_t)> fun);
129 
139  void register_listener(CommListener &listener);
140 
141 protected:
142 
143  static constexpr size_t READ_BUFFER_SIZE = 1024;
144  static constexpr size_t WRITE_BUFFER_SIZE = 1024;
145 
146  static DefaultMessageHandler default_message_handler_;
147 
148  virtual bool is_open() = 0;
149  virtual bool do_init() = 0;
150  virtual void do_close() = 0;
151  virtual void do_async_read(const boost::asio::mutable_buffers_1 &buffer,
152  boost::function<void(const boost::system::error_code&, size_t)> handler) = 0;
153  virtual void do_async_write(const boost::asio::const_buffers_1 &buffer,
154  boost::function<void(const boost::system::error_code&, size_t)> handler) = 0;
155 
156  MessageHandler& message_handler_;
157  boost::asio::io_service io_service_;
158 
159 private:
160 
161  struct ReadBuffer
162  {
163  uint8_t data[READ_BUFFER_SIZE];
164  size_t len;
165 
166  ReadBuffer(const uint8_t * buf, size_t len) : len(len)
167  {
168  assert(len <= READ_BUFFER_SIZE); // only checks in debug mode
169  memcpy(data, buf, len);
170  }
171  };
172 
173  struct WriteBuffer
174  {
175  uint8_t data[WRITE_BUFFER_SIZE];
176  size_t len;
177  size_t pos;
178 
179  WriteBuffer() : len(0), pos(0) {}
180 
181  WriteBuffer(const uint8_t * buf, size_t len) : len(len), pos(0)
182  {
183  assert(len <= WRITE_BUFFER_SIZE); // only checks in debug mode
184  memcpy(data, buf, len);
185  }
186 
187  const uint8_t * dpos() const { return data + pos; }
188 
189  size_t nbytes() const { return len - pos; }
190  };
191 
192  typedef std::lock_guard<std::recursive_mutex> mutex_lock;
193 
194  void async_read();
195  void async_read_end(const boost::system::error_code& error, size_t bytes_transferred);
196 
197  void async_write(bool check_write_state);
198  void async_write_end(const boost::system::error_code& error, size_t bytes_transferred);
199 
200  void process_callbacks();
201 
202  std::thread io_thread_;
203  std::thread callback_thread_;
204 
205  uint8_t read_buffer_[READ_BUFFER_SIZE];
206  std::list<ReadBuffer> read_queue_;
207  std::mutex callback_mutex_;
208  std::condition_variable condition_variable_;
209  bool new_data_;
210  bool shutdown_requested_;
211 
212  std::list<WriteBuffer> write_queue_;
213  std::recursive_mutex write_mutex_;
214  bool write_in_progress_;
215 
216  std::function<void(const uint8_t *, size_t)> receive_callback_ = nullptr;
217  std::vector<std::reference_wrapper<CommListener>> listeners_;
218 };
219 
220 } // namespace async_comm
221 
222 #endif // ASYNC_COMM_COMM_H
Abstract base class for getting comm events via a listener interface.
Definition: comm.h:61
virtual void receive_callback(const uint8_t *buf, size_t size)=0
Callback for data received.
Abstract base class for an asynchronous communication port.
Definition: comm.h:81
void send_byte(uint8_t data)
Send a single byte over the port.
Definition: comm.h:113
Abstract base class for message handler.
Default message handler that outputs to stdout and stderr.
std::mutex mutex
mutex for synchronization between the main thread and callback thread
std::condition_variable condition_variable
condition variable used to suspend main thread until all messages have been received back ...