JaiaBot 2.7.0
JaiaBot micro-AUV software
 
Loading...
Searching...
No Matches
serial_fletcher16.h
Go to the documentation of this file.
1
2#ifndef JAIABOT_LORA_SERIAL_H
3#define JAIABOT_LORA_SERIAL_H
4
5#include <boost/asio.hpp>
6#include <string>
7
8#include <goby/middleware/io/detail/io_interface.h>
9#include <goby/version.h>
10#include <goby/middleware/io/detail/serial_interface.h>
11
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 lora
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 =
44constexpr auto CRC_SIZE = 4;
45
49template <const goby::middleware::Group& line_in_group,
50 const goby::middleware::Group& line_out_group,
51 goby::middleware::io::PubSubLayer publish_layer =
52 goby::middleware::io::PubSubLayer::INTERPROCESS,
53 goby::middleware::io::PubSubLayer subscribe_layer =
54 goby::middleware::io::PubSubLayer::INTERTHREAD,
55#if GOBY_VERSION_MAJOR == 3 && GOBY_VERSION_MINOR < 4
56 template <class> class ThreadType = goby::middleware::SimpleThread,
57#else
58 template <class> class ThreadType = goby::zeromq::SimpleThread,
59#endif
60 bool use_indexed_groups = false>
62 : public goby::middleware::io::detail::SerialThread<line_in_group, line_out_group,
63 publish_layer, subscribe_layer, ThreadType,
64 use_indexed_groups>
65{
66 using Base =
67 goby::middleware::io::detail::SerialThread<line_in_group, line_out_group, publish_layer,
68 subscribe_layer, ThreadType, use_indexed_groups>;
69
70 public:
71 SerialThreadFletcher16(const goby::middleware::protobuf::SerialConfig& config, int index = -1)
72 : Base(config, index)
73 {
74 }
75
77
78 private:
79 void async_read() override;
80
81 void read_first_byte();
82 void read_magic();
83 void read_size();
84 void read_body();
85
86 private:
87 std::array<char, SERIAL_MAX_SIZE> buffer_;
88 char* buffer_write_ptr_{buffer_.data()};
89 std::uint16_t message_size_{0};
90};
91
92template <typename ProtobufMessage>
93ProtobufMessage parse(const goby::middleware::protobuf::IOData& io)
94{
95 auto& data = io.data();
96 ProtobufMessage pb_msg;
98
99 // Check CRC32
100 // auto calculated_crc = crc32(&data[0] + prefix_size, data.size() - prefix_size - CRC_SIZE);
101 // auto crc = *((uint32_t *) (&data[0] + data.size() - CRC_SIZE));
102
103 // if (calculated_crc != crc) {
104 // throw std::string("CRC mismatch!");
105 // }
106
107 pb_msg.ParseFromArray(&data[0] + prefix_size, data.size() - prefix_size);
108 return pb_msg;
109}
110
111template <typename ProtobufMessage>
112std::shared_ptr<goby::middleware::protobuf::IOData> serialize(const ProtobufMessage& pb_msg)
113{
114 auto io = std::make_shared<goby::middleware::protobuf::IOData>();
115
116 std::string pb_encoded = pb_msg.SerializeAsString();
117
118 std::uint16_t size = pb_encoded.size();
119 std::string size_str = {static_cast<char>((size >> jaiabot::lora::BITS_IN_BYTE) & 0xFF),
120 static_cast<char>(size & 0xFF)};
121
122 auto header_and_data = jaiabot::lora::SERIAL_MAGIC + size_str + pb_encoded;
123
124 // Add CRC32
125 auto crc = fletcher16(&pb_encoded[0], pb_encoded.size());
126 std::string crc_data = std::string((const char*)&crc, sizeof(crc));
127
128 // FAKE NOISE
129 // header_and_data[std::rand() % header_and_data.size()] = std::rand() % 256;
130
131 io->set_data(header_and_data + crc_data);
132
133 return io;
134}
135
136} // namespace lora
137} // namespace jaiabot
138
139template <const goby::middleware::Group& line_in_group,
140 const goby::middleware::Group& line_out_group,
141 goby::middleware::io::PubSubLayer publish_layer,
142 goby::middleware::io::PubSubLayer subscribe_layer, template <class> class ThreadType,
143 bool use_indexed_groups>
144void jaiabot::lora::SerialThreadFletcher16<line_in_group, line_out_group, publish_layer,
145 subscribe_layer, ThreadType,
146 use_indexed_groups>::async_read()
147{
148 buffer_write_ptr_ = buffer_.data();
149 read_first_byte();
150}
151
152template <const goby::middleware::Group& line_in_group,
153 const goby::middleware::Group& line_out_group,
154 goby::middleware::io::PubSubLayer publish_layer,
155 goby::middleware::io::PubSubLayer subscribe_layer, template <class> class ThreadType,
156 bool use_indexed_groups>
157void jaiabot::lora::SerialThreadFletcher16<line_in_group, line_out_group, publish_layer,
158 subscribe_layer, ThreadType,
159 use_indexed_groups>::read_first_byte()
160{
161 boost::asio::async_read(
162 this->mutable_serial_port(),
163 boost::asio::buffer(buffer_write_ptr_,
164 buffer_.size() - (buffer_write_ptr_ - buffer_.data())),
165 boost::asio::transfer_exactly(1),
166 [this](const boost::system::error_code& ec, std::size_t bytes_transferred) {
167 if (!ec && bytes_transferred > 0)
168 {
169 if (buffer_[0] != SERIAL_MAGIC[0])
170 {
171 goby::glog.is_warn() &&
172 goby::glog << "Invalid first byte, expected: " << SERIAL_MAGIC[0]
173 << ", received: " << buffer_[0] << std::endl;
174 this->async_read();
175 }
176 else
177 {
178 buffer_write_ptr_ += bytes_transferred;
179 read_magic();
180 }
181 }
182 else
183 {
184 this->handle_read_error(ec);
185 }
186 });
187}
188
189template <const goby::middleware::Group& line_in_group,
190 const goby::middleware::Group& line_out_group,
191 goby::middleware::io::PubSubLayer publish_layer,
192 goby::middleware::io::PubSubLayer subscribe_layer, template <class> class ThreadType,
193 bool use_indexed_groups>
194void jaiabot::lora::SerialThreadFletcher16<line_in_group, line_out_group, publish_layer,
195 subscribe_layer, ThreadType,
196 use_indexed_groups>::read_magic()
197{
198 boost::asio::async_read(
199 this->mutable_serial_port(),
200 boost::asio::buffer(buffer_write_ptr_,
201 buffer_.size() - (buffer_write_ptr_ - buffer_.data())),
202 boost::asio::transfer_exactly(SERIAL_MAGIC_BYTES - 1),
203 [this](const boost::system::error_code& ec, std::size_t bytes_transferred) {
204 if (!ec && bytes_transferred > 0)
205 {
206 if (memcmp(buffer_.data(), SERIAL_MAGIC, SERIAL_MAGIC_BYTES) != 0)
207 {
208 goby::glog.is_warn() &&
209 goby::glog
210 << "Invalid magic word, expected: " << SERIAL_MAGIC << ", received: "
211 << std::string(buffer_.data(), buffer_.data() + SERIAL_MAGIC_BYTES)
212 << std::endl;
213 this->async_read();
214 }
215 else
216 {
217 buffer_write_ptr_ += bytes_transferred;
218 read_size();
219 }
220 }
221 else
222 {
223 this->handle_read_error(ec);
224 }
225 });
226}
227
228template <const goby::middleware::Group& line_in_group,
229 const goby::middleware::Group& line_out_group,
230 goby::middleware::io::PubSubLayer publish_layer,
231 goby::middleware::io::PubSubLayer subscribe_layer, template <class> class ThreadType,
232 bool use_indexed_groups>
233void jaiabot::lora::SerialThreadFletcher16<line_in_group, line_out_group, publish_layer,
234 subscribe_layer, ThreadType,
235 use_indexed_groups>::read_size()
236{
237 boost::asio::async_read(
238 this->mutable_serial_port(),
239 boost::asio::buffer(buffer_write_ptr_,
240 buffer_.size() - (buffer_write_ptr_ - buffer_.data())),
241 boost::asio::transfer_exactly(SIZE_BYTES),
242 [this](const boost::system::error_code& ec, std::size_t bytes_transferred) {
243 if (!ec && bytes_transferred > 0)
244 {
245 message_size_ = 0;
246 message_size_ |= buffer_[SERIAL_MAGIC_BYTES];
247 message_size_ <<= BITS_IN_BYTE;
248 message_size_ |= buffer_[SERIAL_MAGIC_BYTES + 1];
249 if (message_size_ > jaiabot_protobuf_LoRaMessage_size)
250 {
251 goby::glog.is_warn() && goby::glog
252 << "Reported message size is larger than Protobuf "
253 "LoraMessage maximum size. Reported:"
254 << message_size_ << ", expected max: "
255 << jaiabot_protobuf_LoRaMessage_size << std::endl;
256
257 this->async_read();
258 }
259 else
260 {
261 buffer_write_ptr_ += bytes_transferred;
262 read_body();
263 }
264 }
265 else
266 {
267 this->handle_read_error(ec);
268 }
269 });
270}
271
272template <const goby::middleware::Group& line_in_group,
273 const goby::middleware::Group& line_out_group,
274 goby::middleware::io::PubSubLayer publish_layer,
275 goby::middleware::io::PubSubLayer subscribe_layer, template <class> class ThreadType,
276 bool use_indexed_groups>
277void jaiabot::lora::SerialThreadFletcher16<line_in_group, line_out_group, publish_layer,
278 subscribe_layer, ThreadType,
279 use_indexed_groups>::read_body()
280{
281 boost::asio::async_read(
282 this->mutable_serial_port(),
283 boost::asio::buffer(buffer_write_ptr_,
284 buffer_.size() - (buffer_write_ptr_ - buffer_.data())),
285 boost::asio::transfer_exactly(message_size_),
286 [this](const boost::system::error_code& ec, std::size_t bytes_transferred) {
287 if (!ec && bytes_transferred > 0)
288 {
289 // Need to check fletcher-16 checksum for message integrity here
290
291 buffer_write_ptr_ += bytes_transferred;
292 auto io_msg = std::make_shared<goby::middleware::protobuf::IOData>();
293 io_msg->set_data(std::string(buffer_.data(), buffer_write_ptr_));
294 this->handle_read_success(buffer_write_ptr_ - buffer_.data(), io_msg);
295 this->async_read();
296 }
297 else
298 {
299 this->handle_read_error(ec);
300 }
301 });
302}
303
304#endif
Reads/Writes LoRa Adafruit feather message packages from/to serial port.
SerialThreadFletcher16(const goby::middleware::protobuf::SerialConfig &config, int index=-1)
uint16_t fletcher16(const void *input_str, size_t num_bytes)
Calculate the Fletcher-16 checksum of a block of data.
Definition fletcher16.h:9
Definition crc32.h:11
ProtobufMessage parse(const goby::middleware::protobuf::IOData &io)
constexpr auto CRC_SIZE
constexpr int SERIAL_MAGIC_BYTES
constexpr const char * SERIAL_MAGIC
std::shared_ptr< goby::middleware::protobuf::IOData > serialize(const ProtobufMessage &pb_msg)
constexpr int BITS_IN_BYTE
constexpr auto SERIAL_MAX_SIZE
constexpr int SIZE_BYTES