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