JaiaBot  1.20.0
JaiaBot micro-AUV software
serial_crc32.h
Go to the documentation of this file.
1 
2 #ifndef JAIABOT_SERIAL_CRC32_H
3 #define JAIABOT_SERIAL_CRC32_H
4 
5 #include <boost/asio.hpp>
6 #include <string>
7 
8 #include "goby/middleware/io/detail/io_interface.h" // for PubSubLayer
9 #include "goby/middleware/io/detail/serial_interface.h" // for SerialThread
10 
11 #include "jaiabot/crc/crc32.h"
12 
14 
15 namespace goby
16 {
17 namespace middleware
18 {
19 class Group;
20 }
21 } // namespace goby
22 namespace goby
23 {
24 namespace middleware
25 {
26 namespace protobuf
27 {
28 class SerialConfig;
29 }
30 } // namespace middleware
31 } // namespace goby
32 
33 namespace jaiabot
34 {
35 namespace serial
36 {
37 constexpr const char* SERIAL_MAGIC = "JAIA";
38 constexpr int SERIAL_MAGIC_BYTES = 4;
39 constexpr int SIZE_BYTES = 2;
40 constexpr int BITS_IN_BYTE = 8;
41 constexpr auto SERIAL_MAX_SIZE = 2048; // Can be increased, just for safety
42 constexpr auto CRC_SIZE = 4;
43 
44 static const std::shared_ptr<goby::middleware::protobuf::IOData>
45 encode_frame(const std::string& data)
46 {
47  std::uint16_t size = data.length();
48  std::string size_str = {static_cast<char>((size >> jaiabot::serial::BITS_IN_BYTE) & 0xFF),
49  static_cast<char>(size & 0xFF)};
50 
51  auto crc = calculate_crc32(data.c_str(), size);
52  char crc_data[4];
53  for (int i = 0; i < 4; i++) { crc_data[i] = ((char*)&crc)[3 - i]; }
54 
55  auto io_data = std::make_shared<goby::middleware::protobuf::IOData>();
56  io_data->set_data(std::string(SERIAL_MAGIC, SERIAL_MAGIC_BYTES) + size_str +
57  string(crc_data, 4) + data);
58 
59  return io_data;
60 }
61 
62 static const std::string decode_frame(const std::string& frame_data)
63 {
64  const auto HEADER_LENGTH = SERIAL_MAGIC_BYTES + SIZE_BYTES + CRC_SIZE;
65  auto size = frame_data.length() - HEADER_LENGTH;
66 
67  if (size >= 0)
68  {
69  return frame_data.substr(HEADER_LENGTH, size);
70  }
71  else
72  {
73  return "";
74  }
75 }
76 
80 template <const goby::middleware::Group& line_in_group,
81  const goby::middleware::Group& line_out_group,
82  goby::middleware::io::PubSubLayer publish_layer =
83  goby::middleware::io::PubSubLayer::INTERPROCESS,
84  goby::middleware::io::PubSubLayer subscribe_layer =
85  goby::middleware::io::PubSubLayer::INTERTHREAD,
86  template <class> class ThreadType = goby::middleware::SimpleThread,
87  bool use_indexed_groups = false>
89  : public goby::middleware::io::detail::SerialThread<line_in_group, line_out_group,
90  publish_layer, subscribe_layer, ThreadType,
91  use_indexed_groups>
92 {
93  using Base =
94  goby::middleware::io::detail::SerialThread<line_in_group, line_out_group, publish_layer,
95  subscribe_layer, ThreadType, use_indexed_groups>;
96 
97  public:
98  SerialThreadCRC32(const goby::middleware::protobuf::SerialConfig& config, int index = -1)
99  : Base(config, index)
100  {
101  }
102 
104 
105  private:
106  void async_read() override
107  {
108  buffer_write_ptr_ = buffer_.data();
109  read_first_byte();
110  }
111 
112  void read_first_byte()
113  {
114  boost::asio::async_read(
115  this->mutable_serial_port(),
116  boost::asio::buffer(buffer_write_ptr_,
117  buffer_.size() - (buffer_write_ptr_ - buffer_.data())),
118  boost::asio::transfer_exactly(1),
119  [this](const boost::system::error_code& ec, std::size_t bytes_transferred) {
120  if (!ec && bytes_transferred > 0)
121  {
122  if (buffer_[0] != SERIAL_MAGIC[0])
123  {
124  goby::glog.is_warn() &&
125  goby::glog << "Invalid first byte, expected: " << SERIAL_MAGIC[0]
126  << ", received: " << buffer_[0] << std::endl;
127  this->async_read();
128  }
129  else
130  {
131  buffer_write_ptr_ += bytes_transferred;
132  read_magic();
133  }
134  }
135  else
136  {
137  this->handle_read_error(ec);
138  }
139  });
140  }
141 
142  void read_magic()
143  {
144  boost::asio::async_read(
145  this->mutable_serial_port(),
146  boost::asio::buffer(buffer_write_ptr_,
147  buffer_.size() - (buffer_write_ptr_ - buffer_.data())),
148  boost::asio::transfer_exactly(SERIAL_MAGIC_BYTES - 1),
149  [this](const boost::system::error_code& ec, std::size_t bytes_transferred) {
150  if (!ec && bytes_transferred > 0)
151  {
152  if (memcmp(buffer_.data(), SERIAL_MAGIC, SERIAL_MAGIC_BYTES) != 0)
153  {
154  goby::glog.is_warn() &&
155  goby::glog
156  << "Invalid magic word, expected: " << SERIAL_MAGIC
157  << ", received: "
158  << std::string(buffer_.data(), buffer_.data() + SERIAL_MAGIC_BYTES)
159  << std::endl;
160  this->async_read();
161  }
162  else
163  {
164  buffer_write_ptr_ += bytes_transferred;
165  read_size();
166  }
167  }
168  else
169  {
170  this->handle_read_error(ec);
171  }
172  });
173  }
174 
175  void read_size()
176  {
177  boost::asio::async_read(
178  this->mutable_serial_port(),
179  boost::asio::buffer(buffer_write_ptr_,
180  buffer_.size() - (buffer_write_ptr_ - buffer_.data())),
181  boost::asio::transfer_exactly(SIZE_BYTES),
182  [this](const boost::system::error_code& ec, std::size_t bytes_transferred) {
183  if (!ec && bytes_transferred > 0)
184  {
185  message_size_ = 0;
186  message_size_ |= buffer_[SERIAL_MAGIC_BYTES];
187  message_size_ <<= BITS_IN_BYTE;
188  message_size_ |= buffer_[SERIAL_MAGIC_BYTES + 1];
189  if (message_size_ > SERIAL_MAX_SIZE)
190  {
191  goby::glog.is_warn() &&
192  goby::glog
193  << "Reported message size is larger than SERIAL_MAX_SIZE. Reported:"
194  << message_size_ << ", expected max: " << SERIAL_MAX_SIZE
195  << std::endl;
196 
197  this->async_read();
198  }
199  else
200  {
201  buffer_write_ptr_ += bytes_transferred;
202  read_crc32();
203  }
204  }
205  else
206  {
207  this->handle_read_error(ec);
208  }
209  });
210  }
211 
212  void read_crc32()
213  {
214  boost::asio::async_read(
215  this->mutable_serial_port(),
216  boost::asio::buffer(buffer_write_ptr_,
217  buffer_.size() - (buffer_write_ptr_ - buffer_.data())),
218  boost::asio::transfer_exactly(CRC_SIZE),
219  [this](const boost::system::error_code& ec, std::size_t bytes_transferred) {
220  if (!ec && bytes_transferred > 0)
221  {
222  crc32_ = 0;
223  char* ptr = reinterpret_cast<char*>(&crc32_);
224 
225  // Manually construct the integer from the byte array
226  for (int i = 0; i < 4; i++) { ptr[3 - i] = buffer_write_ptr_[i]; }
227 
228  buffer_write_ptr_ += bytes_transferred;
229  read_body();
230  }
231  else
232  {
233  this->handle_read_error(ec);
234  }
235  });
236  }
237 
238  void read_body()
239  {
240  boost::asio::async_read(
241  this->mutable_serial_port(),
242  boost::asio::buffer(buffer_write_ptr_,
243  buffer_.size() - (buffer_write_ptr_ - buffer_.data())),
244  boost::asio::transfer_exactly(message_size_),
245  [this](const boost::system::error_code& ec, std::size_t bytes_transferred) {
246  if (!ec && bytes_transferred > 0)
247  {
248  auto actual_crc32 = calculate_crc32(buffer_write_ptr_, bytes_transferred);
249 
250  if (crc32_ != actual_crc32)
251  {
252  goby::glog.is_warn() &&
253  goby::glog << "message_size_: " << message_size_
254  << ", bytes_transferred: " << bytes_transferred
255  << ", expected crc32: " << crc32_
256  << ", actual_crc32: " << actual_crc32 << std::endl;
257 
258  goby::glog.is_warn() && goby::glog << "CRC32 failure. Expected: " << crc32_
259  << ", actual: " << actual_crc32
260  << std::endl;
261 
262  this->async_read();
263  }
264 
265  buffer_write_ptr_ += bytes_transferred;
266 
267  auto io_msg = std::make_shared<goby::middleware::protobuf::IOData>();
268  io_msg->set_data(std::string(buffer_.data(), buffer_write_ptr_));
269  this->handle_read_success(buffer_write_ptr_ - buffer_.data(), io_msg);
270  this->async_read();
271  }
272  else
273  {
274  this->handle_read_error(ec);
275  }
276  });
277  }
278 
279  private:
280  std::array<char, SERIAL_MAX_SIZE> buffer_;
281  char* buffer_write_ptr_{buffer_.data()};
282  std::uint16_t message_size_{0};
283  std::uint32_t crc32_{0};
284 };
285 
286 } // namespace serial
287 } // namespace jaiabot
288 
289 #endif
Reads/Writes message packages from/to serial port.
Definition: serial_crc32.h:92
SerialThreadCRC32(const goby::middleware::protobuf::SerialConfig &config, int index=-1)
Definition: serial_crc32.h:98
Definition: crc32.h:11
uint32_t calculate_crc32(const void *buf, size_t len, uint32_t initial=0)
Definition: crc32.h:43
constexpr int SERIAL_MAGIC_BYTES
Definition: serial_crc32.h:38
static const std::string decode_frame(const std::string &frame_data)
Definition: serial_crc32.h:62
constexpr auto CRC_SIZE
Definition: serial_crc32.h:42
constexpr int SIZE_BYTES
Definition: serial_crc32.h:39
constexpr const char * SERIAL_MAGIC
Definition: serial_crc32.h:37
constexpr int BITS_IN_BYTE
Definition: serial_crc32.h:40
static const std::shared_ptr< goby::middleware::protobuf::IOData > encode_frame(const std::string &data)
Definition: serial_crc32.h:45
constexpr auto SERIAL_MAX_SIZE
Definition: serial_crc32.h:41
extern ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier< ::goby::acomms::protobuf::DriverConfig, ::PROTOBUF_NAMESPACE_ID::internal::MessageTypeTraits< ::jaiabot::udp::protobuf::Config >, 11, false > config