JaiaBot 2.0.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" // for PubSubLayer
9#include "goby/middleware/io/detail/serial_interface.h" // for SerialThread
10
11#include "jaiabot/crc/crc32.h"
12
14
15namespace goby
16{
17namespace middleware
18{
19class Group;
20}
21} // namespace goby
22namespace goby
23{
24namespace middleware
25{
26namespace protobuf
27{
28class SerialConfig;
29}
30} // namespace middleware
31} // namespace goby
32
33namespace jaiabot
34{
35namespace serial
36{
37constexpr const char* SERIAL_MAGIC = "JAIA";
38constexpr int SERIAL_MAGIC_BYTES = 4;
39constexpr int SIZE_BYTES = 2;
40constexpr int BITS_IN_BYTE = 8;
41constexpr auto SERIAL_MAX_SIZE = 2048; // Can be increased, just for safety
42constexpr auto CRC_SIZE = 4;
43
44static const std::shared_ptr<goby::middleware::protobuf::IOData>
45encode_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
62static 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
80template <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.
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)