1use crate::{
4 Dialect, MAVLinkMessageRaw, MAVLinkV1MessageRaw, MAVLinkV2MessageRaw, MavHeader,
5 MavlinkVersion, Message, SigningData,
6 error::MessageReadError,
7 frame_decoder::{FrameDecoder, FrameRef, VersionFilter},
8 static_dialect::StaticDialect,
9};
10
11#[cfg(all(feature = "embedded", not(feature = "std")))]
12use embedded_io::Read;
13#[cfg(feature = "std")]
14use std::io::Read;
15
16pub struct MavlinkReader<R> {
22 source: R,
23 decoder: FrameDecoder,
24}
25
26impl<R> MavlinkReader<R> {
27 #[cfg(feature = "std")]
31 pub fn new(source: R) -> Self
32 where
33 R: Read,
34 {
35 Self {
36 source,
37 decoder: FrameDecoder::new(),
38 }
39 }
40
41 #[cfg(all(feature = "embedded", not(feature = "std")))]
43 pub const fn new(source: R) -> Self
44 where
45 R: Read,
46 {
47 Self {
48 source,
49 decoder: FrameDecoder::new(),
50 }
51 }
52
53 #[cfg(feature = "std")]
59 pub fn with_capacity(capacity: usize, source: R) -> Self
60 where
61 R: Read,
62 {
63 Self {
64 source,
65 decoder: FrameDecoder::with_capacity(capacity),
66 }
67 }
68
69 pub const fn get_ref(&self) -> &R {
71 &self.source
72 }
73
74 pub const fn get_mut(&mut self) -> &mut R {
79 &mut self.source
80 }
81
82 pub fn into_inner(self) -> R {
86 self.source
87 }
88}
89
90#[cfg(any(feature = "std", all(feature = "embedded", not(feature = "std"))))]
91impl<R: Read> MavlinkReader<R> {
92 pub fn read_message<M: Message>(
94 &mut self,
95 version: MavlinkVersion,
96 ) -> Result<(MavHeader, M), MessageReadError> {
97 self.read_message_inner(VersionFilter::Exact(version), None)
98 }
99
100 pub fn read_message_with_dialect<D: Dialect>(
102 &mut self,
103 dialect: &D,
104 version: MavlinkVersion,
105 ) -> Result<(MavHeader, D::Message), MessageReadError> {
106 self.read_message_with_dialect_inner(VersionFilter::Exact(version), dialect, None)
107 }
108
109 pub fn read_any_message<M: Message>(&mut self) -> Result<(MavHeader, M), MessageReadError> {
111 self.read_message_inner(VersionFilter::Any, None)
112 }
113
114 pub fn read_any_message_with_dialect<D: Dialect>(
116 &mut self,
117 dialect: &D,
118 ) -> Result<(MavHeader, D::Message), MessageReadError> {
119 self.read_message_with_dialect_inner(VersionFilter::Any, dialect, None)
120 }
121
122 pub fn read_raw_message<M: Message>(
124 &mut self,
125 version: MavlinkVersion,
126 ) -> Result<MAVLinkMessageRaw, MessageReadError> {
127 self.read_raw_message_inner::<M>(VersionFilter::Exact(version), None)
128 }
129
130 pub fn read_raw_message_with_dialect<D: Dialect>(
132 &mut self,
133 dialect: &D,
134 version: MavlinkVersion,
135 ) -> Result<MAVLinkMessageRaw, MessageReadError> {
136 self.read_raw_message_with_dialect_inner(VersionFilter::Exact(version), dialect, None)
137 }
138
139 pub fn read_any_raw_message<M: Message>(
141 &mut self,
142 ) -> Result<MAVLinkMessageRaw, MessageReadError> {
143 self.read_raw_message_inner::<M>(VersionFilter::Any, None)
144 }
145
146 pub fn read_any_raw_message_with_dialect<D: Dialect>(
148 &mut self,
149 dialect: &D,
150 ) -> Result<MAVLinkMessageRaw, MessageReadError> {
151 self.read_raw_message_with_dialect_inner(VersionFilter::Any, dialect, None)
152 }
153
154 #[cfg(feature = "mav2-message-signing")]
160 pub fn read_message_signed<M: Message>(
161 &mut self,
162 version: MavlinkVersion,
163 signing_data: Option<&SigningData>,
164 ) -> Result<(MavHeader, M), MessageReadError> {
165 self.read_message_inner(VersionFilter::Exact(version), signing_data)
166 }
167
168 #[cfg(feature = "mav2-message-signing")]
170 pub fn read_message_signed_with_dialect<D: Dialect>(
171 &mut self,
172 dialect: &D,
173 version: MavlinkVersion,
174 signing_data: Option<&SigningData>,
175 ) -> Result<(MavHeader, D::Message), MessageReadError> {
176 self.read_message_with_dialect_inner(VersionFilter::Exact(version), dialect, signing_data)
177 }
178
179 #[cfg(feature = "mav2-message-signing")]
183 pub fn read_any_message_signed<M: Message>(
184 &mut self,
185 signing_data: Option<&SigningData>,
186 ) -> Result<(MavHeader, M), MessageReadError> {
187 self.read_message_inner(VersionFilter::Any, signing_data)
188 }
189
190 #[cfg(feature = "mav2-message-signing")]
192 pub fn read_any_message_signed_with_dialect<D: Dialect>(
193 &mut self,
194 dialect: &D,
195 signing_data: Option<&SigningData>,
196 ) -> Result<(MavHeader, D::Message), MessageReadError> {
197 self.read_message_with_dialect_inner(VersionFilter::Any, dialect, signing_data)
198 }
199
200 #[cfg(feature = "mav2-message-signing")]
206 pub fn read_raw_message_signed<M: Message>(
207 &mut self,
208 version: MavlinkVersion,
209 signing_data: Option<&SigningData>,
210 ) -> Result<MAVLinkMessageRaw, MessageReadError> {
211 self.read_raw_message_inner::<M>(VersionFilter::Exact(version), signing_data)
212 }
213
214 #[cfg(feature = "mav2-message-signing")]
216 pub fn read_raw_message_signed_with_dialect<D: Dialect>(
217 &mut self,
218 dialect: &D,
219 version: MavlinkVersion,
220 signing_data: Option<&SigningData>,
221 ) -> Result<MAVLinkMessageRaw, MessageReadError> {
222 self.read_raw_message_with_dialect_inner(
223 VersionFilter::Exact(version),
224 dialect,
225 signing_data,
226 )
227 }
228
229 #[cfg(feature = "mav2-message-signing")]
233 pub fn read_any_raw_message_signed<M: Message>(
234 &mut self,
235 signing_data: Option<&SigningData>,
236 ) -> Result<MAVLinkMessageRaw, MessageReadError> {
237 self.read_raw_message_inner::<M>(VersionFilter::Any, signing_data)
238 }
239
240 #[cfg(feature = "mav2-message-signing")]
242 pub fn read_any_raw_message_signed_with_dialect<D: Dialect>(
243 &mut self,
244 dialect: &D,
245 signing_data: Option<&SigningData>,
246 ) -> Result<MAVLinkMessageRaw, MessageReadError> {
247 self.read_raw_message_with_dialect_inner(VersionFilter::Any, dialect, signing_data)
248 }
249
250 #[inline]
251 pub(crate) fn read_message_inner<M: Message>(
252 &mut self,
253 filter: VersionFilter,
254 signing_data: Option<&SigningData>,
255 ) -> Result<(MavHeader, M), MessageReadError> {
256 self.read_message_with_dialect_inner(filter, &StaticDialect::<M>::new(), signing_data)
257 }
258
259 #[inline]
260 pub(crate) fn read_message_with_dialect_inner<D: Dialect>(
261 &mut self,
262 filter: VersionFilter,
263 dialect: &D,
264 signing_data: Option<&SigningData>,
265 ) -> Result<(MavHeader, D::Message), MessageReadError> {
266 loop {
267 if let Some(result) =
268 try_decode_message(&mut self.decoder, filter, dialect, signing_data)
269 {
270 return result;
271 }
272 self.read_more()?;
273 }
274 }
275
276 #[inline]
277 pub(crate) fn read_raw_message_inner<M: Message>(
278 &mut self,
279 filter: VersionFilter,
280 signing_data: Option<&SigningData>,
281 ) -> Result<MAVLinkMessageRaw, MessageReadError> {
282 self.read_raw_message_with_dialect_inner(filter, &StaticDialect::<M>::new(), signing_data)
283 }
284
285 #[inline]
286 pub(crate) fn read_raw_message_with_dialect_inner<D: Dialect>(
287 &mut self,
288 filter: VersionFilter,
289 dialect: &D,
290 signing_data: Option<&SigningData>,
291 ) -> Result<MAVLinkMessageRaw, MessageReadError> {
292 loop {
293 if let Some(message) =
294 try_decode_raw_message(&mut self.decoder, filter, dialect, signing_data)
295 {
296 return Ok(message);
297 }
298 self.read_more()?;
299 }
300 }
301
302 fn read_more(&mut self) -> Result<(), MessageReadError> {
303 let destination = self.decoder.spare_capacity_mut();
304 assert!(
305 !destination.is_empty(),
306 "a pending MAVLink frame must fit in the decoder buffer"
307 );
308
309 #[cfg(feature = "std")]
310 let count = loop {
311 match self.source.read(destination) {
312 Err(error) if error.kind() == std::io::ErrorKind::Interrupted => continue,
313 result => break result?,
314 }
315 };
316
317 #[cfg(all(feature = "embedded", not(feature = "std")))]
318 let count = self
319 .source
320 .read(destination)
321 .map_err(|_| MessageReadError::Io)?;
322
323 if count == 0 {
324 return Err(MessageReadError::eof());
325 }
326 self.decoder.commit(count);
327 Ok(())
328 }
329}
330
331#[inline(always)]
332pub(crate) fn try_decode_message<D: Dialect>(
333 decoder: &mut FrameDecoder,
334 filter: VersionFilter,
335 dialect: &D,
336 signing_data: Option<&SigningData>,
337) -> Option<Result<(MavHeader, D::Message), MessageReadError>> {
338 #[allow(clippy::never_loop)]
339 loop {
340 let meta = decoder.next_frame(filter, dialect)?;
341
342 #[cfg(feature = "mav2-message-signing")]
343 if let Some(signing_data) = signing_data
344 && !signature_is_valid(decoder.frame(meta), signing_data)
345 {
346 decoder.advance(meta);
347 continue;
348 }
349
350 #[cfg(not(feature = "mav2-message-signing"))]
351 let _ = signing_data;
352
353 let (header, parsed) = {
354 let frame = decoder.frame(meta);
355 (
356 frame_header(frame),
357 dialect.decode(frame.version(), frame.message_id(), frame.payload()),
358 )
359 };
360 decoder.advance(meta);
361 return Some(parsed.map(|message| (header, message)).map_err(Into::into));
362 }
363}
364
365#[inline]
366pub(crate) fn try_decode_raw_message<D: Dialect>(
367 decoder: &mut FrameDecoder,
368 filter: VersionFilter,
369 dialect: &D,
370 signing_data: Option<&SigningData>,
371) -> Option<MAVLinkMessageRaw> {
372 #[allow(clippy::never_loop)]
373 loop {
374 let meta = decoder.next_frame(filter, dialect)?;
375 let frame = decoder.frame(meta);
376
377 let message = match frame.version() {
378 MavlinkVersion::V1 => MAVLinkMessageRaw::V1(MAVLinkV1MessageRaw::from(frame)),
379 MavlinkVersion::V2 => MAVLinkMessageRaw::V2(MAVLinkV2MessageRaw::from(frame)),
380 };
381
382 #[cfg(feature = "mav2-message-signing")]
383 if let Some(signing_data) = signing_data
384 && !raw_signature_is_valid(&message, signing_data)
385 {
386 decoder.advance(meta);
387 continue;
388 }
389
390 #[cfg(not(feature = "mav2-message-signing"))]
391 let _ = signing_data;
392
393 decoder.advance(meta);
394 return Some(message);
395 }
396}
397
398#[inline]
399fn frame_header(frame: FrameRef<'_>) -> MavHeader {
400 MavHeader {
401 sequence: frame.sequence(),
402 system_id: frame.system_id(),
403 component_id: frame.component_id(),
404 }
405}
406
407#[cfg(feature = "mav2-message-signing")]
408fn signature_is_valid(frame: FrameRef<'_>, signing_data: &SigningData) -> bool {
409 match frame.version() {
410 MavlinkVersion::V1 => signing_data.config.allow_unsigned,
411 MavlinkVersion::V2 => signing_data.verify_signature(&MAVLinkV2MessageRaw::from(frame)),
412 }
413}
414
415#[cfg(feature = "mav2-message-signing")]
416fn raw_signature_is_valid(message: &MAVLinkMessageRaw, signing_data: &SigningData) -> bool {
417 match message {
418 MAVLinkMessageRaw::V1(_) => signing_data.config.allow_unsigned,
419 MAVLinkMessageRaw::V2(message) => signing_data.verify_signature(message),
420 }
421}
422
423#[cfg(all(test, feature = "std"))]
424mod tests {
425 use super::*;
426 use std::io;
427
428 #[derive(Debug, PartialEq, Eq)]
429 struct TestMessage(u8);
430
431 impl Message for TestMessage {
432 fn message_id(&self) -> u32 {
433 0
434 }
435
436 fn message_name(&self) -> &'static str {
437 "TEST"
438 }
439
440 fn target_system_id(&self) -> Option<u8> {
441 None
442 }
443
444 fn target_component_id(&self) -> Option<u8> {
445 None
446 }
447
448 fn target_system_id_mut(&mut self) -> Option<&mut u8> {
449 None
450 }
451
452 fn target_component_id_mut(&mut self) -> Option<&mut u8> {
453 None
454 }
455
456 fn ser(&self, _: MavlinkVersion, bytes: &mut [u8]) -> usize {
457 bytes[0] = self.0;
458 1
459 }
460
461 fn parse(_: MavlinkVersion, _: u32, payload: &[u8]) -> Result<Self, crate::ParserError> {
462 Ok(Self(payload[0]))
463 }
464
465 fn message_id_from_name(_: &str) -> Option<u32> {
466 None
467 }
468
469 fn default_message_from_id(_: u32) -> Option<Self> {
470 None
471 }
472
473 #[cfg(feature = "arbitrary")]
474 fn random_message_from_id<R: rand::TryRng<Error = core::convert::Infallible>>(
475 _: u32,
476 _: &mut R,
477 ) -> Option<Self> {
478 None
479 }
480
481 fn extra_crc(_: u32) -> u8 {
482 77
483 }
484 }
485
486 struct CountingReader {
487 bytes: std::vec::Vec<u8>,
488 offset: usize,
489 reads: usize,
490 }
491
492 impl Read for CountingReader {
493 fn read(&mut self, destination: &mut [u8]) -> io::Result<usize> {
494 self.reads += 1;
495 let count = destination.len().min(self.bytes.len() - self.offset);
496 destination[..count].copy_from_slice(&self.bytes[self.offset..self.offset + count]);
497 self.offset += count;
498 Ok(count)
499 }
500 }
501
502 #[test]
503 fn standard_reader_capacity_is_configurable_and_frame_safe() {
504 let mut default_reader = MavlinkReader::new(io::empty());
505 assert_eq!(
506 default_reader.decoder.spare_capacity_mut().len(),
507 crate::consts::DEFAULT_READ_BUFFER_CAPACITY
508 );
509
510 let mut minimum_reader = MavlinkReader::with_capacity(1, io::empty());
511 assert_eq!(
512 minimum_reader.decoder.spare_capacity_mut().len(),
513 crate::consts::MAX_FRAME_SIZE
514 );
515 }
516
517 #[test]
518 fn reads_multiple_frames_per_source_read() {
519 let header = MavHeader {
520 sequence: 1,
521 system_id: 2,
522 component_id: 3,
523 };
524 let mut bytes = std::vec::Vec::new();
525 for value in 0..16 {
526 let mut raw = MAVLinkV2MessageRaw::new();
527 raw.serialize_message(header, &TestMessage(value));
528 bytes.extend_from_slice(raw.raw_bytes());
529 }
530
531 let mut reader = MavlinkReader::new(CountingReader {
532 bytes,
533 offset: 0,
534 reads: 0,
535 });
536 for value in 0..16 {
537 let (_, message) = reader.read_any_message::<TestMessage>().unwrap();
538 assert_eq!(message, TestMessage(value));
539 }
540 assert_eq!(reader.get_ref().reads, 1);
541 }
542}