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