Skip to main content

mavlink_core/
lib.rs

1//! The MAVLink message set.
2//!
3//! # Message sets and the `Message` trait
4//! Each message set has its own module with corresponding data types, including a `MavMessage` enum
5//! that represents all possible messages in that message set. The [`Message`] trait is used to
6//! represent messages in an abstract way, and each `MavMessage` enum implements this trait (for
7//! example, [ardupilotmega::MavMessage]). This is then monomorphized to the specific message
8//! set you are using in your application at compile-time via type parameters. If you expect
9//! ArduPilotMega-flavored messages, then you will need a `MavConnection<ardupilotmega::MavMessage>`
10//! and you will receive `ardupilotmega::MavMessage`s from it.
11//!
12//! Some message sets include others. For example, most message sets include the
13//! common message set. These included values are not differently represented in the `MavMessage` enum: a message
14//! in the common message set received on an ArduPilotMega connection will just be an
15//! `ardupilotmega::MavMessage`.
16//!
17//! If you want to enable a given message set, you do not have to enable the
18//! feature for the message sets that it includes. For example, you can use the `dialect-ardupilotmega`
19//! feature without also using the `dialect-uavionix`, `dialect-icarous`, `dialect-common` features.
20//!
21//! [ardupilotmega::MavMessage]: https://docs.rs/mavlink/latest/mavlink/ardupilotmega/enum.MavMessage.html
22//!
23//! # Reading
24//!
25//! Use [`MavlinkReader`] for blocking input and [`AsyncMavlinkReader`] for
26//! asynchronous input. Both readers retain partial frames and read-ahead data
27//! between calls, discard invalid candidates, and verify message CRCs before
28//! returning.
29//!
30//! [`MavlinkReader`]: crate::MavlinkReader
31//!
32//! # Write Functions
33//!
34//! The `write_` functions are used to write a MAVLink to a [`Write`]r.
35//! They follow the pattern `write_(v1|v2|versioned)_msg[_async][_signed](..)`:
36//!
37//! - `v1` functions write messages using MAVLink 1 serialisation
38//! - `v2` functions write messages using MAVLink 2 serialisation
39//! - `versioned` functions write messages using the version specified in an aditional `version` parameter
40//! - `_async` functions, which are only enabled with the `tokio` feature, are
41//!   [async](https://doc.rust-lang.org/std/keyword.async.html) and write to a `tokio::io::AsyncWrite` instead.
42//! - `_signed` functions, which are only enabled with the `mav2-message-signing` feature, have an `Option<&SigningData>` parameter that allows the use of MAVLink 2 message signing.
43//!
44//! ## Write errors
45//!
46//! All `write_` functions return `Result<_,` [`MessageWriteError`]`>`.
47//!
48//! - When an error other than `std::io::ErrorKind::Interrupted` occurs on the
49//!   underlying writer, the function returns [`MessageWriteError::Io`]
50//! - When attempting to serialize a message with an ID over 255 with MAVLink 1 a [`MessageWriteError::MAVLink2Only`] is returned
51//!
52#![cfg_attr(not(feature = "std"), no_std)]
53#![cfg_attr(docsrs, feature(doc_cfg))]
54#![deny(clippy::all)]
55#![warn(clippy::use_self)]
56
57use core::result::Result;
58
59#[cfg(feature = "std")]
60use std::io::Write;
61
62pub mod utils;
63#[allow(unused_imports)]
64use utils::{RustDefault, remove_trailing_zeroes};
65
66#[cfg(feature = "serde")]
67use serde::{Deserialize, Serialize};
68
69use crate::frame_decoder::FrameRef;
70use crate::{
71    bytes::Bytes,
72    error::{EncoderError, MessageWriteError, ParserError},
73};
74
75use crc_any::CRCu16;
76
77#[doc(hidden)]
78pub mod bytes;
79#[doc(hidden)]
80pub mod bytes_mut;
81#[cfg(any(feature = "std", feature = "tokio"))]
82mod connection;
83pub mod consts;
84pub mod error;
85mod frame_decoder;
86mod reader;
87#[doc(hidden)]
88pub mod static_dialect;
89pub mod types;
90#[cfg(feature = "std")]
91pub use self::connection::{
92    Connectable, Connection, DialectConnection, MavConnection, connect, connect_with_dialect,
93};
94pub use reader::MavlinkReader;
95
96#[cfg(feature = "tokio")]
97pub use self::connection::{
98    AsyncConnectable, AsyncConnection, AsyncDialectConnection, AsyncMavConnection, connect_async,
99    connect_async_with_dialect,
100};
101
102#[cfg(feature = "tokio")]
103use tokio::io::{AsyncWrite, AsyncWriteExt};
104
105#[cfg(any(feature = "tokio", all(feature = "embedded", not(feature = "std"))))]
106mod async_reader;
107#[cfg(any(feature = "tokio", all(feature = "embedded", not(feature = "std"))))]
108pub use async_reader::AsyncMavlinkReader;
109
110#[cfg(all(feature = "embedded", not(feature = "std")))]
111pub mod embedded;
112#[cfg(all(feature = "embedded", not(feature = "std")))]
113use embedded::Write;
114
115#[cfg(not(feature = "mav2-message-signing"))]
116type SigningData = ();
117#[cfg(feature = "mav2-message-signing")]
118mod signing;
119#[cfg(feature = "mav2-message-signing")]
120pub use self::signing::{SigningConfig, SigningData};
121#[cfg(feature = "mav2-message-signing")]
122use sha2::{Digest, Sha256};
123
124#[cfg(feature = "arbitrary")]
125use arbitrary::Arbitrary;
126
127#[cfg(any(feature = "std", feature = "tokio"))]
128mod connectable;
129
130#[cfg(any(feature = "std", feature = "tokio"))]
131mod connection_shared;
132
133#[cfg(any(feature = "std", feature = "tokio"))]
134pub use connectable::ConnectionAddress;
135
136#[cfg(feature = "transport-direct-serial")]
137pub use connection::direct_serial::config::SerialConfig;
138
139#[cfg(feature = "transport-tcp")]
140pub use connection::tcp::config::{TcpConfig, TcpMode};
141
142#[cfg(feature = "transport-udp")]
143pub use connection::udp::config::{UdpConfig, UdpMode};
144
145#[cfg(feature = "std")]
146pub use connection::file::config::FileConfig;
147
148/// A MAVLink message payload
149///
150/// Each message sets `MavMessage` enum implements this trait. The [`Message`] trait is used to
151/// represent messages in an abstract way (for example, `common::MavMessage`).
152pub trait Message
153where
154    Self: Sized,
155{
156    /// MAVLink message ID
157    fn message_id(&self) -> u32;
158
159    /// MAVLink message name
160    fn message_name(&self) -> &'static str;
161
162    /// Target system ID if the message is directed to a specific system
163    fn target_system_id(&self) -> Option<u8>;
164
165    /// Target component ID if the message is directed to a specific component
166    fn target_component_id(&self) -> Option<u8>;
167
168    /// Mutable reference to the target system ID if the message is directed to a specific system
169    ///
170    /// Covers the same messages as [`Message::target_system_id`]. Lets a router rewrite the
171    /// addressed system in place, e.g. when translating between system IDs.
172    fn target_system_id_mut(&mut self) -> Option<&mut u8>;
173
174    /// Mutable reference to the target component ID if the message is directed to a specific
175    /// component
176    ///
177    /// Covers the same messages as [`Message::target_component_id`].
178    fn target_component_id_mut(&mut self) -> Option<&mut u8>;
179
180    /// Serialize **Message** into byte slice and return count of bytes written
181    ///
182    /// # Panics
183    ///
184    /// Will panic if the buffer provided is to small to store this message
185    fn ser(&self, version: MavlinkVersion, bytes: &mut [u8]) -> usize;
186
187    /// Parse a Message from its message id and payload bytes
188    ///
189    /// # Errors
190    ///
191    /// - [`UnknownMessage`] if the given message id is not part of the dialect
192    /// - any other [`ParserError`] returned by the individual message deserialization
193    ///
194    /// [`UnknownMessage`]: ParserError::UnknownMessage
195    fn parse(version: MavlinkVersion, msgid: u32, payload: &[u8]) -> Result<Self, ParserError>;
196
197    /// Return message id of specific message name
198    fn message_id_from_name(name: &str) -> Option<u32>;
199    /// Return a default message of the speicfied message id
200    fn default_message_from_id(id: u32) -> Option<Self>;
201    /// Return random valid message of the speicfied message id
202    #[cfg(feature = "arbitrary")]
203    fn random_message_from_id<R: rand::TryRng<Error = std::convert::Infallible>>(
204        id: u32,
205        rng: &mut R,
206    ) -> Option<Self>;
207    /// Return a message types [CRC_EXTRA byte](https://mavlink.io/en/guide/serialization.html#crc_extra)
208    fn extra_crc(id: u32) -> u8;
209}
210
211pub trait MessageData: Sized {
212    type Message: Message;
213
214    const ID: u32;
215    const NAME: &'static str;
216    const EXTRA_CRC: u8;
217    const ENCODED_LEN: usize;
218
219    /// # Panics
220    ///
221    /// Will panic if the buffer provided is to small to hold the full message payload of the implementing message type
222    fn ser(&self, version: MavlinkVersion, payload: &mut [u8]) -> usize;
223    /// # Errors
224    ///
225    /// Will return [`ParserError::InvalidEnum`] on a nonexistent enum value and
226    fn deser(version: MavlinkVersion, payload: &[u8]) -> Result<Self, ParserError>;
227}
228
229/// An instance-backed MAVLink dialect.
230///
231/// Unlike [`Message`], a dialect can obtain its message definitions at runtime.
232/// Implementations provide the CRC extra byte required to validate a frame and
233/// decode its payload into their message representation.
234///
235/// The generated, compile-time dialect API continues to use [`Message`] and
236/// is adapted internally to this trait by the reader implementation.
237pub trait Dialect {
238    /// The representation returned when a message is decoded.
239    type Message;
240
241    /// Error returned when encoding a message.
242    type EncodeError: Into<EncoderError>;
243
244    /// MAVLink message ID for an encoded message.
245    fn message_id(&self, message: &Self::Message) -> u32;
246
247    /// Return the CRC extra byte for `message_id`, if the message is known.
248    ///
249    /// This lookup is required before decoding because MAVLink incorporates
250    /// the CRC extra byte into the frame checksum.
251    fn extra_crc(&self, message_id: u32) -> Option<u8>;
252
253    /// Decode a validated MAVLink payload.
254    fn decode(
255        &self,
256        version: MavlinkVersion,
257        message_id: u32,
258        payload: &[u8],
259    ) -> Result<Self::Message, ParserError>;
260
261    /// Serialize a dialect message payload for the selected MAVLink version.
262    fn encode(
263        &self,
264        version: MavlinkVersion,
265        message: &Self::Message,
266        payload: &mut [u8],
267    ) -> Result<usize, Self::EncodeError>;
268}
269
270/// Metadata from a MAVLink packet header
271#[derive(Debug, Copy, Clone, PartialEq, Eq)]
272#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
273#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
274pub struct MavHeader {
275    /// Sender system ID, values over 255 are only supported for MAVLink 2 and the require receiver support them.
276    pub system_id: u32,
277    /// Sender component ID
278    pub component_id: u8,
279    /// Packet sequence number
280    pub sequence: u8,
281}
282
283/// [Versions of the MAVLink](https://mavlink.io/en/guide/mavlink_version.html) protocol that we support
284#[derive(Debug, Copy, Clone, PartialEq, Eq)]
285#[cfg_attr(feature = "serde", derive(Serialize))]
286#[cfg_attr(feature = "serde", serde(tag = "type"))]
287#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
288pub enum MavlinkVersion {
289    /// Version v1.0
290    V1,
291    /// Version v2.0
292    V2,
293}
294
295/// Message framing marker for MAVLink 1
296pub const MAV_STX: u8 = 0xFE;
297
298/// Message framing marker for MAVLink 2
299pub const MAV_STX_V2: u8 = 0xFD;
300
301/// Return a default GCS header, seq is replaced by the connector
302/// so it can be ignored. Set `component_id` to your desired component ID.
303impl Default for MavHeader {
304    fn default() -> Self {
305        Self {
306            system_id: 255,
307            component_id: 0,
308            sequence: 0,
309        }
310    }
311}
312
313/// Encapsulation of the MAVLink message and the header,
314/// important to preserve information about the sender system
315/// and component id.
316#[derive(Debug, Clone)]
317#[cfg_attr(feature = "serde", derive(Serialize))]
318#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
319pub struct MavFrame<M: Message> {
320    /// Message header data
321    pub header: MavHeader,
322    /// Parsed [`Message`] payload
323    pub msg: M,
324    /// Messages MAVLink version
325    pub protocol_version: MavlinkVersion,
326}
327
328impl<M: Message> MavFrame<M> {
329    /// Serialize MavFrame into a byte slice, so it can be sent over a socket, for example.
330    /// The resulting buffer will start with the sequence field of the MAVLink frame
331    /// and will not include the initial packet marker, length field, and flags.
332    ///
333    /// # Panics
334    ///
335    /// - If the frame does not fit in the provided buffer
336    /// - When attempting to serialize a message with an id greater then 255 with MAVLink 1
337    /// - When attempting to serialize a header with an system id greater then 255 with MAVLink 1
338    pub fn ser(&self, buf: &mut [u8]) -> usize {
339        let mut buf = bytes_mut::BytesMut::new(buf);
340
341        // Currently expects a buffer with the sequence field at the start.
342        // If this is updated to include the initial packet marker, length field, and flags,
343        // uncomment.
344        //
345        // match self.protocol_version {
346        //     MavlinkVersion::V2 => {
347        //         buf.put_u8(MAV_STX_V2);
348        //         buf.put_u8(payload_len as u8);
349        //         but.put_u8(0); // incompatibility flags
350        //         buf.put_u8(0); // compatibility flags
351        //     }
352        //     MavlinkVersion::V1 => {
353        //         buf.put_u8(MAV_STX);
354        //         buf.put_u8(payload_len as u8);
355        //     }
356        // }
357
358        // serialize header
359        if let Ok(sys_id_8) = self.header.system_id.try_into() {
360            buf.put_slice(&[self.header.sequence, sys_id_8, self.header.component_id]);
361        } else {
362            buf.put_u8(self.header.sequence);
363            match self.protocol_version {
364                MavlinkVersion::V2 => buf.put_u32_le(self.header.system_id),
365                MavlinkVersion::V1 => panic!("system IDs over 255 are MAVLink 2 only"),
366            }
367            buf.put_u8(self.header.component_id);
368        }
369
370        // message id
371        match self.protocol_version {
372            MavlinkVersion::V2 => buf.put_u24_le(self.msg.message_id()),
373            MavlinkVersion::V1 => buf.put_u8(
374                self.msg
375                    .message_id()
376                    .try_into()
377                    .expect("message is MAVLink 2 only"),
378            ),
379        }
380
381        let header_len = buf.len();
382        // Serialize the payload straight into the destination buffer right after
383        // the header and avoid an intermediate buffer and an extra copy.
384        let payload_len = self.msg.ser(self.protocol_version, &mut buf[header_len..]);
385
386        header_len + payload_len
387    }
388
389    /// Deserialize MavFrame from a slice that has been received from, for example, a socket.
390    /// The input buffer should start with the sequence field of the MAVLink frame. The
391    /// initial packet marker, length field, and flag fields should be excluded. This function
392    /// does not produce correct results if the header has the `IFLAG_SYSID32` flag set.  
393    ///
394    /// # Errors
395    ///
396    /// Will return a [`ParserError`] if a message was found but could not be parsed
397    /// or the if the buffer provided does not contain a full message
398    #[deprecated = "Use deser_with_flags to support the IFLAG_SYSID32 incompatebility flag"]
399    pub fn deser(version: MavlinkVersion, input: &[u8]) -> Result<Self, ParserError> {
400        Self::deser_with_flags(version, 0, input)
401    }
402
403    /// Deserialize MavFrame from a slice that has been received from, for example, a socket.
404    /// The input buffer should start with the sequence field of the MAVLink frame. The
405    /// initial packet marker, length field, and flag fields should be excluded.
406    ///
407    /// # Errors
408    ///
409    /// Will return a [`ParserError`] if a message was found but could not be parsed
410    /// or the if the buffer provided does not contain a full message
411    pub fn deser_with_flags(
412        version: MavlinkVersion,
413        incompat_flags: u8,
414        input: &[u8],
415    ) -> Result<Self, ParserError> {
416        let mut buf = Bytes::new(input);
417
418        // Currently expects a buffer with the sequence field at the start.
419        // If this is updated to include the initial packet marker, length field, and flags,
420        // uncomment.
421        // <https://mavlink.io/en/guide/serialization.html#mavlink2_packet_format>
422        // match version {
423        //     MavlinkVersion::V2 => buf.get_u32_le(),
424        //     MavlinkVersion::V1 => buf.get_u16_le().into(),
425        // };
426
427        let sequence = buf.get_u8()?;
428        let system_id = if incompat_flags & consts::v2::IFLAG_SYSID32 != 0 {
429            buf.get_u32_le()?
430        } else {
431            buf.get_u8()? as u32
432        };
433        let component_id = buf.get_u8()?;
434        let header = MavHeader {
435            system_id,
436            component_id,
437            sequence,
438        };
439
440        let msg_id = match version {
441            MavlinkVersion::V2 => buf.get_u24_le()?,
442            MavlinkVersion::V1 => buf.get_u8()?.into(),
443        };
444
445        M::parse(version, msg_id, buf.remaining_bytes()).map(|msg| Self {
446            header,
447            msg,
448            protocol_version: version,
449        })
450    }
451
452    /// Return the frame header
453    pub fn header(&self) -> MavHeader {
454        self.header
455    }
456}
457
458/// Calculates the [CRC checksum](https://mavlink.io/en/guide/serialization.html#checksum) of a messages header, payload and the CRC_EXTRA byte.
459pub fn calculate_crc(data: &[u8], extra_crc: u8) -> u16 {
460    let mut crc_calculator = CRCu16::crc16mcrf4cc();
461    crc_calculator.digest(data);
462
463    crc_calculator.digest(&[extra_crc]);
464    crc_calculator.get_crc()
465}
466
467/// Byte buffer containing the raw representation of a MAVLink 1 message beginning with the STX marker.
468///
469/// Follow protocol definition: <https://mavlink.io/en/guide/serialization.html#v1_packet_format>.
470/// Maximum size is 263 bytes.
471#[derive(Debug, Copy, Clone, PartialEq, Eq)]
472pub struct MAVLinkV1MessageRaw([u8; consts::v1::FRAME_SIZE]);
473
474impl MAVLinkV1MessageRaw {
475    /// Create a new raw MAVLink 1 message filled with zeros.
476    pub const fn new() -> Self {
477        Self([0; consts::v1::FRAME_SIZE])
478    }
479
480    /// Create a new raw MAVLink 1 message from a given buffer.
481    ///
482    /// Note: This method does not guarantee that the constructed MAVLink message is valid.
483    pub const fn from_bytes_unparsed(bytes: [u8; consts::v1::FRAME_SIZE]) -> Self {
484        Self(bytes)
485    }
486
487    /// Read access to its internal buffer.
488    #[inline]
489    pub fn as_slice(&self) -> &[u8] {
490        &self.0
491    }
492
493    /// Mutable reference to its internal buffer.
494    #[inline]
495    pub fn as_mut_slice(&mut self) -> &mut [u8] {
496        &mut self.0
497    }
498
499    /// Deconstruct the MAVLink message into its owned internal buffer.
500    #[inline]
501    pub fn into_inner(self) -> [u8; consts::v1::FRAME_SIZE] {
502        self.0
503    }
504
505    /// Reference to the 5 byte header slice of the message
506    #[inline]
507    pub fn header(&self) -> &[u8] {
508        &self.0[consts::STX_SIZE..(consts::STX_SIZE + consts::v1::HEADER_SIZE)]
509    }
510
511    /// Mutable reference to the 5 byte header slice of the message
512    #[inline]
513    fn mut_header(&mut self) -> &mut [u8] {
514        &mut self.0[consts::STX_SIZE..(consts::STX_SIZE + consts::v1::HEADER_SIZE)]
515    }
516
517    /// Size of the payload of the message
518    #[inline]
519    pub fn payload_length(&self) -> u8 {
520        self.0[consts::PAYLOAD_LEN_OFFSET]
521    }
522
523    /// Packet sequence number
524    #[inline]
525    pub fn sequence(&self) -> u8 {
526        self.0[2]
527    }
528
529    /// Message sender System ID
530    #[inline]
531    pub fn system_id(&self) -> u8 {
532        self.0[3]
533    }
534
535    /// Message sender Component ID
536    #[inline]
537    pub fn component_id(&self) -> u8 {
538        self.0[4]
539    }
540
541    /// Message ID
542    #[inline]
543    pub fn message_id(&self) -> u8 {
544        self.0[5]
545    }
546
547    /// Reference to the payload byte slice of the message
548    #[inline]
549    pub fn payload(&self) -> &[u8] {
550        let payload_length: usize = self.payload_length().into();
551        let payload_offset = consts::STX_SIZE + consts::v1::HEADER_SIZE;
552        &self.0[payload_offset..(payload_offset + payload_length)]
553    }
554
555    /// [CRC-16 checksum](https://mavlink.io/en/guide/serialization.html#checksum) field of the message
556    #[inline]
557    pub fn checksum(&self) -> u16 {
558        let payload_length: usize = self.payload_length().into();
559        let checksum_offset = consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length;
560        u16::from_le_bytes([self.0[checksum_offset], self.0[checksum_offset + 1]])
561    }
562
563    /// Checks wether the message’s [CRC-16 checksum](https://mavlink.io/en/guide/serialization.html#checksum) calculation matches its checksum field.
564    #[inline]
565    pub fn has_valid_crc<M: Message>(&self) -> bool {
566        self.has_valid_crc_with_extra_crc(M::extra_crc(self.message_id().into()))
567    }
568
569    /// Checks whether this message's checksum matches a supplied MAVLink CRC
570    /// extra byte. This is useful for dialects loaded at runtime.
571    #[inline]
572    pub fn has_valid_crc_with_extra_crc(&self, extra_crc: u8) -> bool {
573        let payload_length: usize = self.payload_length().into();
574        let crc_end = consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length;
575        self.checksum() == calculate_crc(&self.0[consts::STX_SIZE..crc_end], extra_crc)
576    }
577
578    /// Raw byte slice of the message
579    pub fn raw_bytes(&self) -> &[u8] {
580        let payload_length = self.payload_length() as usize;
581        let frame_len =
582            consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length + consts::CHECKSUM_SIZE;
583        &self.0[..frame_len]
584    }
585
586    /// # Panics
587    ///
588    /// If the `msgid` parameter exceeds 255 and is therefore not supported for MAVLink 1
589    fn serialize_stx_and_header_and_crc(
590        &mut self,
591        header: MavHeader,
592        msgid: u32,
593        payload_length: usize,
594        extra_crc: u8,
595    ) {
596        self.0[consts::STX_OFFSET] = MAV_STX;
597
598        let header_buf = self.mut_header();
599        header_buf.copy_from_slice(&[
600            payload_length as u8,
601            header.sequence,
602            header.system_id as u8,
603            header.component_id,
604            msgid.try_into().unwrap(),
605        ]);
606
607        let crc = calculate_crc(
608            &self.0
609                [consts::STX_SIZE..(consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length)],
610            extra_crc,
611        );
612        self.0[(consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length)
613            ..(consts::STX_SIZE
614                + consts::v1::HEADER_SIZE
615                + payload_length
616                + consts::CHECKSUM_SIZE)]
617            .copy_from_slice(&crc.to_le_bytes());
618    }
619
620    /// Serialize a [`Message`] with a given header into this raw message buffer.
621    ///
622    /// # Panics
623    ///
624    /// If the header's system id or the message's id exceeds 255 and is therefore not supported for MAVLink 1
625    pub fn serialize_message<M: Message>(&mut self, header: MavHeader, message: &M) {
626        assert!(header.system_id <= u8::MAX as u32);
627        let payload_offset = consts::STX_SIZE + consts::v1::HEADER_SIZE;
628        let payload_buf = &mut self.0[payload_offset..(payload_offset + consts::MAX_PAYLOAD_LEN)];
629        let payload_length = message.ser(MavlinkVersion::V1, payload_buf);
630
631        let message_id = message.message_id();
632        self.serialize_stx_and_header_and_crc(
633            header,
634            message_id,
635            payload_length,
636            M::extra_crc(message_id),
637        );
638    }
639
640    /// Serialize a message through an instance-backed dialect.
641    pub fn serialize_message_with_dialect<D: Dialect>(
642        &mut self,
643        header: MavHeader,
644        dialect: &D,
645        message: &D::Message,
646    ) -> Result<(), EncoderError> {
647        if header.system_id > u8::MAX as u32 {
648            return Err(EncoderError::InvalidMAVLink1SystemId {
649                id: header.system_id,
650            });
651        }
652        let payload_offset = consts::STX_SIZE + consts::v1::HEADER_SIZE;
653        let payload = &mut self.0[payload_offset..(payload_offset + consts::MAX_PAYLOAD_LEN)];
654        let message_id = dialect.message_id(message);
655        let payload_length = dialect
656            .encode(MavlinkVersion::V1, message, payload)
657            .map_err(Into::into)?;
658        let extra_crc = dialect
659            .extra_crc(message_id)
660            .ok_or(EncoderError::UnknownMessage { id: message_id })?;
661        self.serialize_stx_and_header_and_crc(header, message_id, payload_length, extra_crc);
662        Ok(())
663    }
664
665    /// # Panics
666    ///
667    /// If the `MavHeader`'s sytem_id or the `MessageData`'s `ID` exceeds 255 and is therefore not supported for MAVLink 1
668    pub fn serialize_message_data<D: MessageData>(&mut self, header: MavHeader, message_data: &D) {
669        assert!(header.system_id <= u8::MAX as u32);
670        let payload_offset = consts::STX_SIZE + consts::v1::HEADER_SIZE;
671        let payload_buf = &mut self.0[payload_offset..(payload_offset + consts::MAX_PAYLOAD_LEN)];
672        let payload_length = message_data.ser(MavlinkVersion::V1, payload_buf);
673
674        self.serialize_stx_and_header_and_crc(header, D::ID, payload_length, D::EXTRA_CRC);
675    }
676}
677
678impl Default for MAVLinkV1MessageRaw {
679    fn default() -> Self {
680        Self::new()
681    }
682}
683
684impl<'a> From<FrameRef<'a>> for MAVLinkV1MessageRaw {
685    fn from(frame: FrameRef<'a>) -> Self {
686        let mut message = Self::new();
687        message.0[..frame.bytes().len()].copy_from_slice(frame.bytes());
688        message
689    }
690}
691
692/// Byte buffer containing the raw representation of a MAVLink 2 message beginning with the STX marker.
693///
694/// Follow protocol definition: <https://mavlink.io/en/guide/serialization.html#mavlink2_packet_format>.
695/// Maximum size is [280 bytes](consts::MAX_FRAME_SIZE).
696#[derive(Debug, Copy, Clone, PartialEq, Eq)]
697pub struct MAVLinkV2MessageRaw([u8; consts::MAX_FRAME_SIZE]);
698
699impl MAVLinkV2MessageRaw {
700    /// Create a new raw MAVLink 2 message filled with zeros.
701    pub const fn new() -> Self {
702        Self([0; consts::MAX_FRAME_SIZE])
703    }
704
705    /// Create a new raw MAVLink 2 message from a given buffer.
706    ///
707    /// Note: This method does not guarantee that the constructed MAVLink message is valid.
708    pub const fn from_bytes_unparsed(bytes: [u8; consts::MAX_FRAME_SIZE]) -> Self {
709        Self(bytes)
710    }
711
712    /// Read access to its internal buffer.
713    #[inline]
714    pub fn as_slice(&self) -> &[u8] {
715        &self.0
716    }
717
718    /// Mutable reference to its internal buffer.
719    #[inline]
720    pub fn as_mut_slice(&mut self) -> &mut [u8] {
721        &mut self.0
722    }
723
724    /// Deconstruct the MAVLink message into its owned internal buffer.
725    #[inline]
726    pub fn into_inner(self) -> [u8; consts::MAX_FRAME_SIZE] {
727        self.0
728    }
729
730    fn header_size(&self) -> usize {
731        if self.has_32bit_sysid() {
732            consts::v2::MIN_HEADER_SIZE + consts::v2::SYSID32_EXTRA_HEADER_SIZE
733        } else {
734            consts::v2::MIN_HEADER_SIZE
735        }
736    }
737
738    /// Reference to the 9 byte header slice of the message
739    #[inline]
740    pub fn header(&self) -> &[u8] {
741        &self.0[consts::STX_SIZE..(consts::STX_SIZE + self.header_size())]
742    }
743
744    /// Mutable reference to the header byte slice of the message
745    #[inline]
746    fn mut_header(&mut self) -> &mut [u8] {
747        let header_size = self.header_size();
748        &mut self.0[consts::STX_SIZE..(consts::STX_SIZE + header_size)]
749    }
750
751    /// Size of the payload of the message
752    #[inline]
753    pub fn payload_length(&self) -> u8 {
754        self.0[consts::PAYLOAD_LEN_OFFSET]
755    }
756
757    /// [Incompatiblity flags](https://mavlink.io/en/guide/serialization.html#incompat_flags) of the message
758    ///
759    /// Currently the only supported incompatebility flags are `consts::v2::IFLAG_SIGNED` and `consts::v2::IFLAG_SYSID32`.
760    #[inline]
761    pub fn incompatibility_flags(&self) -> u8 {
762        self.0[consts::v2::INCOMPAT_FLAGS_OFFSET]
763    }
764
765    /// Mutable reference to the [incompatiblity flags](https://mavlink.io/en/guide/serialization.html#incompat_flags) of the message
766    ///
767    /// Currently the only supported incompatebility flags are `consts::v2::IFLAG_SIGNED` and `consts::v2::IFLAG_SYSID32`.
768    #[inline]
769    pub fn incompatibility_flags_mut(&mut self) -> &mut u8 {
770        &mut self.0[consts::v2::INCOMPAT_FLAGS_OFFSET]
771    }
772
773    fn has_32bit_sysid(&self) -> bool {
774        self.incompatibility_flags() & consts::v2::IFLAG_SYSID32 != 0
775    }
776
777    /// [Compatibility Flags](https://mavlink.io/en/guide/serialization.html#compat_flags) of the message
778    #[inline]
779    pub fn compatibility_flags(&self) -> u8 {
780        self.0[3]
781    }
782
783    /// Packet sequence number
784    #[inline]
785    pub fn sequence(&self) -> u8 {
786        self.0[4]
787    }
788
789    /// Message sender System ID
790    #[inline]
791    pub fn system_id(&self) -> u32 {
792        if self.has_32bit_sysid() {
793            u32::from_le_bytes(self.0[5..=8].try_into().unwrap())
794        } else {
795            self.0[5] as u32
796        }
797    }
798
799    /// Message sender Component ID
800    #[inline]
801    pub fn component_id(&self) -> u8 {
802        if self.has_32bit_sysid() {
803            self.0[9]
804        } else {
805            self.0[6]
806        }
807    }
808
809    /// Message ID
810    #[inline]
811    pub fn message_id(&self) -> u32 {
812        if self.incompatibility_flags() & consts::v2::IFLAG_SYSID32 != 0 {
813            u32::from_le_bytes([self.0[10], self.0[11], self.0[12], 0])
814        } else {
815            u32::from_le_bytes([self.0[7], self.0[8], self.0[9], 0])
816        }
817    }
818
819    fn payload_offset(&self) -> usize {
820        consts::STX_SIZE + self.header_size()
821    }
822
823    /// Reference to the payload byte slice of the message
824    #[inline]
825    pub fn payload(&self) -> &[u8] {
826        let payload_length: usize = self.payload_length().into();
827        let payload_offset = self.payload_offset();
828        &self.0[payload_offset..(payload_offset + payload_length)]
829    }
830
831    /// [CRC-16 checksum](https://mavlink.io/en/guide/serialization.html#checksum) field of the message
832    #[inline]
833    pub fn checksum(&self) -> u16 {
834        let payload_length: usize = self.payload_length().into();
835        let checksum_offset = consts::STX_SIZE + self.header_size() + payload_length;
836        u16::from_le_bytes([self.0[checksum_offset], self.0[checksum_offset + 1]])
837    }
838
839    /// Reference to the 2 checksum bytes of the message
840    #[cfg(feature = "mav2-message-signing")]
841    #[inline]
842    pub fn checksum_bytes(&self) -> &[u8] {
843        let checksum_offset =
844            consts::STX_SIZE + self.header_size() + self.payload_length() as usize;
845        &self.0[checksum_offset..(checksum_offset + consts::CHECKSUM_SIZE)]
846    }
847
848    /// Signature [Link ID](https://mavlink.io/en/guide/message_signing.html#link_ids)
849    ///
850    /// If the message is not signed this 0.
851    #[cfg(feature = "mav2-message-signing")]
852    #[inline]
853    pub fn signature_link_id(&self) -> u8 {
854        let payload_length: usize = self.payload_length().into();
855        self.0[consts::STX_SIZE + self.header_size() + payload_length + consts::CHECKSUM_SIZE]
856    }
857
858    /// Mutable reference to the signature [Link ID](https://mavlink.io/en/guide/message_signing.html#link_ids)
859    #[cfg(feature = "mav2-message-signing")]
860    #[inline]
861    pub fn signature_link_id_mut(&mut self) -> &mut u8 {
862        let payload_length: usize = self.payload_length().into();
863        &mut self.0[consts::STX_SIZE + self.header_size() + payload_length + consts::CHECKSUM_SIZE]
864    }
865
866    /// Message [signature timestamp](https://mavlink.io/en/guide/message_signing.html#timestamp)
867    ///
868    /// The timestamp is a 48 bit number with units of 10 microseconds since 1st January 2015 GMT.
869    /// The offset since 1st January 1970 (the unix epoch) is 1420070400 seconds.
870    /// Since all timestamps generated must be at least 1 more than the previous timestamp this timestamp may get ahead of GMT time if there is a burst of packets at a rate of more than 100000 packets per second.
871    #[cfg(feature = "mav2-message-signing")]
872    #[inline]
873    pub fn signature_timestamp(&self) -> u64 {
874        let mut timestamp_bytes = [0u8; 8];
875        timestamp_bytes[0..consts::v2::SIGNATURE_TIMESTAMP_SIZE]
876            .copy_from_slice(self.signature_timestamp_bytes());
877        u64::from_le_bytes(timestamp_bytes)
878    }
879
880    /// 48 bit [signature timestamp](https://mavlink.io/en/guide/message_signing.html#timestamp) byte slice
881    ///
882    /// If the message is not signed this contains zeros.
883    #[cfg(feature = "mav2-message-signing")]
884    #[inline]
885    pub fn signature_timestamp_bytes(&self) -> &[u8] {
886        let payload_length: usize = self.payload_length().into();
887        let timestamp_start = consts::STX_SIZE
888            + self.header_size()
889            + payload_length
890            + consts::CHECKSUM_SIZE
891            + consts::v2::SIGNATURE_LINK_ID_SIZE;
892        &self.0[timestamp_start..(timestamp_start + consts::v2::SIGNATURE_TIMESTAMP_SIZE)]
893    }
894
895    /// Mutable reference to the 48 bit signature timestams byte slice
896    #[cfg(feature = "mav2-message-signing")]
897    #[inline]
898    pub fn signature_timestamp_bytes_mut(&mut self) -> &mut [u8] {
899        let payload_length: usize = self.payload_length().into();
900        let timestamp_start = consts::STX_SIZE
901            + self.header_size()
902            + payload_length
903            + consts::CHECKSUM_SIZE
904            + consts::v2::SIGNATURE_LINK_ID_SIZE;
905        &mut self.0[timestamp_start..(timestamp_start + consts::v2::SIGNATURE_TIMESTAMP_SIZE)]
906    }
907
908    /// Reference to the 48 bit [message signature](https://mavlink.io/en/guide/message_signing.html#signature) byte slice
909    ///
910    /// If the message is not signed this contains zeros.
911    #[cfg(feature = "mav2-message-signing")]
912    #[inline]
913    pub fn signature_value(&self) -> &[u8] {
914        let payload_length: usize = self.payload_length().into();
915        let signature_start = consts::STX_SIZE
916            + self.header_size()
917            + payload_length
918            + consts::CHECKSUM_SIZE
919            + consts::v2::SIGNATURE_LINK_ID_SIZE
920            + consts::v2::SIGNATURE_TIMESTAMP_SIZE;
921        &self.0[signature_start..(signature_start + consts::v2::SIGNATURE_VALUE_SIZE)]
922    }
923
924    /// Mutable reference to the 48 bit [message signature](https://mavlink.io/en/guide/message_signing.html#signature) byte slice
925    #[cfg(feature = "mav2-message-signing")]
926    #[inline]
927    pub fn signature_value_mut(&mut self) -> &mut [u8] {
928        let payload_length: usize = self.payload_length().into();
929        let signature_start = consts::STX_SIZE
930            + self.header_size()
931            + payload_length
932            + consts::CHECKSUM_SIZE
933            + consts::v2::SIGNATURE_LINK_ID_SIZE
934            + consts::v2::SIGNATURE_TIMESTAMP_SIZE;
935        &mut self.0[signature_start..(signature_start + consts::v2::SIGNATURE_VALUE_SIZE)]
936    }
937
938    /// Checks wether the message's [CRC-16 checksum](https://mavlink.io/en/guide/serialization.html#checksum) calculation matches its checksum field.
939    #[inline]
940    pub fn has_valid_crc<M: Message>(&self) -> bool {
941        self.has_valid_crc_with_extra_crc(M::extra_crc(self.message_id()))
942    }
943
944    /// Checks whether this message's checksum matches a supplied MAVLink CRC
945    /// extra byte. This is useful for dialects loaded at runtime.
946    #[inline]
947    pub fn has_valid_crc_with_extra_crc(&self, extra_crc: u8) -> bool {
948        let payload_length: usize = self.payload_length().into();
949        let crc_end = consts::STX_SIZE + self.header_size() + payload_length;
950        self.checksum() == calculate_crc(&self.0[consts::STX_SIZE..crc_end], extra_crc)
951    }
952
953    /// Calculates the messages sha256_48 signature.
954    ///
955    /// This calculates the [SHA-256](https://en.wikipedia.org/wiki/SHA-2) checksum of messages appended to the 32 byte secret key and copies the first 6 bytes of the result into the target buffer.
956    #[cfg(feature = "mav2-message-signing")]
957    pub fn calculate_signature(
958        &self,
959        secret_key: &[u8],
960        target_buffer: &mut [u8; consts::v2::SIGNATURE_VALUE_SIZE],
961    ) {
962        let mut hasher = Sha256::new();
963        hasher.update(secret_key);
964        hasher.update([MAV_STX_V2]);
965        hasher.update(self.header());
966        hasher.update(self.payload());
967        hasher.update(self.checksum_bytes());
968        hasher.update([self.signature_link_id()]);
969        hasher.update(self.signature_timestamp_bytes());
970        target_buffer.copy_from_slice(&hasher.finalize()[0..consts::v2::SIGNATURE_VALUE_SIZE]);
971    }
972
973    /// Raw byte slice of the message
974    pub fn raw_bytes(&self) -> &[u8] {
975        let payload_length = self.payload_length() as usize;
976
977        let signature_size = if (self.incompatibility_flags() & consts::v2::IFLAG_SIGNED) == 0 {
978            0
979        } else {
980            consts::v2::SIGNATURE_SIZE
981        };
982
983        let frame_len = consts::STX_SIZE
984            + self.header_size()
985            + payload_length
986            + signature_size
987            + consts::CHECKSUM_SIZE;
988        &self.0[..frame_len]
989    }
990
991    fn serialize_stx_and_header_and_crc(
992        &mut self,
993        header: MavHeader,
994        msgid: u32,
995        payload_length: usize,
996        extra_crc: u8,
997        incompat_flags: u8,
998    ) {
999        self.0[consts::STX_OFFSET] = MAV_STX_V2;
1000        let msgid_bytes = msgid.to_le_bytes();
1001
1002        let has_32bit_sysid = self.has_32bit_sysid();
1003        let header_buf = self.mut_header();
1004        if has_32bit_sysid {
1005            let sys_id_bytes = header.system_id.to_le_bytes();
1006            header_buf.copy_from_slice(&[
1007                payload_length as u8,
1008                incompat_flags,
1009                0, //compat_flags
1010                header.sequence,
1011                sys_id_bytes[0],
1012                sys_id_bytes[1],
1013                sys_id_bytes[2],
1014                sys_id_bytes[3],
1015                header.component_id,
1016                msgid_bytes[0],
1017                msgid_bytes[1],
1018                msgid_bytes[2],
1019            ]);
1020        } else {
1021            header_buf.copy_from_slice(&[
1022                payload_length as u8,
1023                incompat_flags,
1024                0, //compat_flags
1025                header.sequence,
1026                header.system_id as u8,
1027                header.component_id,
1028                msgid_bytes[0],
1029                msgid_bytes[1],
1030                msgid_bytes[2],
1031            ]);
1032        }
1033
1034        let header_size = self.header_size();
1035
1036        let crc = calculate_crc(
1037            &self.0[consts::STX_SIZE..(consts::STX_SIZE + header_size + payload_length)],
1038            extra_crc,
1039        );
1040        self.0[(consts::STX_SIZE + header_size + payload_length)
1041            ..(consts::STX_SIZE + header_size + payload_length + consts::CHECKSUM_SIZE)]
1042            .copy_from_slice(&crc.to_le_bytes());
1043    }
1044
1045    /// Serialize a [Message] with a given header into this raw message buffer.
1046    ///
1047    /// This does not set any compatiblity or incompatiblity flags.
1048    pub fn serialize_message<M: Message>(&mut self, header: MavHeader, message: &M) {
1049        if header.system_id > u8::MAX as u32 {
1050            *self.incompatibility_flags_mut() = consts::v2::IFLAG_SYSID32;
1051        }
1052        let payload_offset = self.payload_offset();
1053        let payload_buf = &mut self.0[payload_offset..(payload_offset + consts::MAX_PAYLOAD_LEN)];
1054        let payload_length = message.ser(MavlinkVersion::V2, payload_buf);
1055
1056        let message_id = message.message_id();
1057        self.serialize_stx_and_header_and_crc(
1058            header,
1059            message_id,
1060            payload_length,
1061            M::extra_crc(message_id),
1062            self.incompatibility_flags(),
1063        );
1064    }
1065
1066    /// Serialize a message through an instance-backed dialect.
1067    pub fn serialize_message_with_dialect<D: Dialect>(
1068        &mut self,
1069        header: MavHeader,
1070        dialect: &D,
1071        message: &D::Message,
1072    ) -> Result<(), EncoderError> {
1073        if header.system_id > u8::MAX as u32 {
1074            *self.incompatibility_flags_mut() = consts::v2::IFLAG_SYSID32;
1075        }
1076        let payload_offset = self.payload_offset();
1077        let payload = &mut self.0[payload_offset..(payload_offset + consts::MAX_PAYLOAD_LEN)];
1078        let message_id = dialect.message_id(message);
1079        let payload_length = dialect
1080            .encode(MavlinkVersion::V2, message, payload)
1081            .map_err(Into::into)?;
1082        let extra_crc = dialect
1083            .extra_crc(message_id)
1084            .ok_or(EncoderError::UnknownMessage { id: message_id })?;
1085        self.serialize_stx_and_header_and_crc(
1086            header,
1087            message_id,
1088            payload_length,
1089            extra_crc,
1090            self.incompatibility_flags(),
1091        );
1092        Ok(())
1093    }
1094
1095    /// Serialize a dialect message and mark it for MAVLink 2 signing.
1096    #[cfg(feature = "mav2-message-signing")]
1097    pub fn serialize_message_with_dialect_for_signing<D: Dialect>(
1098        &mut self,
1099        header: MavHeader,
1100        dialect: &D,
1101        message: &D::Message,
1102    ) -> Result<(), EncoderError> {
1103        let iflags = if header.system_id > u8::MAX as u32 {
1104            consts::v2::IFLAG_SYSID32 | consts::v2::IFLAG_SIGNED
1105        } else {
1106            consts::v2::IFLAG_SIGNED
1107        };
1108        *self.incompatibility_flags_mut() = iflags;
1109        let payload_offset = self.payload_offset();
1110        let payload = &mut self.0[payload_offset..(payload_offset + consts::MAX_PAYLOAD_LEN)];
1111        let message_id = dialect.message_id(message);
1112        let payload_length = dialect
1113            .encode(MavlinkVersion::V2, message, payload)
1114            .map_err(Into::into)?;
1115        let extra_crc = dialect
1116            .extra_crc(message_id)
1117            .ok_or(EncoderError::UnknownMessage { id: message_id })?;
1118
1119        self.serialize_stx_and_header_and_crc(
1120            header,
1121            message_id,
1122            payload_length,
1123            extra_crc,
1124            iflags,
1125        );
1126        Ok(())
1127    }
1128
1129    /// Serialize a [Message] with a given header into this raw message buffer and sets the `consts::v2::IFLAG_SIGNED` incompatiblity flag.
1130    ///
1131    /// This does not update the message's signature fields.
1132    /// This does not set any compatiblity flags.
1133    #[cfg(feature = "mav2-message-signing")]
1134    pub fn serialize_message_for_signing<M: Message>(&mut self, header: MavHeader, message: &M) {
1135        let iflags = if header.system_id > u8::MAX as u32 {
1136            consts::v2::IFLAG_SYSID32 | consts::v2::IFLAG_SIGNED
1137        } else {
1138            consts::v2::IFLAG_SIGNED
1139        };
1140        *self.incompatibility_flags_mut() = iflags;
1141        let payload_offset = self.payload_offset();
1142        let payload_buf = &mut self.0[payload_offset..(payload_offset + consts::MAX_PAYLOAD_LEN)];
1143        let payload_length = message.ser(MavlinkVersion::V2, payload_buf);
1144
1145        let message_id = message.message_id();
1146        self.serialize_stx_and_header_and_crc(
1147            header,
1148            message_id,
1149            payload_length,
1150            M::extra_crc(message_id),
1151            iflags,
1152        );
1153    }
1154
1155    pub fn serialize_message_data<D: MessageData>(&mut self, header: MavHeader, message_data: &D) {
1156        if header.system_id > u8::MAX as u32 {
1157            *self.incompatibility_flags_mut() = consts::v2::IFLAG_SYSID32;
1158        }
1159        let payload_offset = self.payload_offset();
1160        let payload_buf = &mut self.0[payload_offset..(payload_offset + consts::MAX_PAYLOAD_LEN)];
1161        let payload_length = message_data.ser(MavlinkVersion::V2, payload_buf);
1162
1163        self.serialize_stx_and_header_and_crc(
1164            header,
1165            D::ID,
1166            payload_length,
1167            D::EXTRA_CRC,
1168            self.incompatibility_flags(),
1169        );
1170    }
1171}
1172
1173impl Default for MAVLinkV2MessageRaw {
1174    fn default() -> Self {
1175        Self::new()
1176    }
1177}
1178
1179impl<'a> From<FrameRef<'a>> for MAVLinkV2MessageRaw {
1180    fn from(frame: FrameRef<'a>) -> Self {
1181        let mut message = Self::new();
1182        message.0[..frame.bytes().len()].copy_from_slice(frame.bytes());
1183        message
1184    }
1185}
1186
1187/// Raw byte representation of a MAVLink message of either version
1188pub enum MAVLinkMessageRaw {
1189    V1(MAVLinkV1MessageRaw),
1190    V2(MAVLinkV2MessageRaw),
1191}
1192
1193impl MAVLinkMessageRaw {
1194    pub fn payload(&self) -> &[u8] {
1195        match self {
1196            Self::V1(msg) => msg.payload(),
1197            Self::V2(msg) => msg.payload(),
1198        }
1199    }
1200    pub fn sequence(&self) -> u8 {
1201        match self {
1202            Self::V1(msg) => msg.sequence(),
1203            Self::V2(msg) => msg.sequence(),
1204        }
1205    }
1206    pub fn system_id(&self) -> u32 {
1207        match self {
1208            Self::V1(msg) => msg.system_id() as u32,
1209            Self::V2(msg) => msg.system_id(),
1210        }
1211    }
1212    pub fn component_id(&self) -> u8 {
1213        match self {
1214            Self::V1(msg) => msg.component_id(),
1215            Self::V2(msg) => msg.component_id(),
1216        }
1217    }
1218    pub fn message_id(&self) -> u32 {
1219        match self {
1220            Self::V1(msg) => u32::from(msg.message_id()),
1221            Self::V2(msg) => msg.message_id(),
1222        }
1223    }
1224    pub fn version(&self) -> MavlinkVersion {
1225        match self {
1226            Self::V1(_) => MavlinkVersion::V1,
1227            Self::V2(_) => MavlinkVersion::V2,
1228        }
1229    }
1230}
1231
1232/// Write a MAVLink message using the given mavlink version to a [`Write`]r.
1233///
1234/// # Errors
1235///
1236/// See [`write_` function error documentation](crate#write-errors).
1237pub fn write_versioned_msg<M: Message, W: Write>(
1238    w: &mut W,
1239    version: MavlinkVersion,
1240    header: MavHeader,
1241    data: &M,
1242) -> Result<usize, MessageWriteError> {
1243    match version {
1244        MavlinkVersion::V2 => write_v2_msg(w, header, data),
1245        MavlinkVersion::V1 => write_v1_msg(w, header, data),
1246    }
1247}
1248
1249/// Write a MAVLink message using an instance-backed dialect.
1250pub fn write_versioned_msg_with_dialect<D: Dialect, W: Write>(
1251    writer: &mut W,
1252    version: MavlinkVersion,
1253    header: MavHeader,
1254    dialect: &D,
1255    message: &D::Message,
1256) -> Result<usize, MessageWriteError> {
1257    match version {
1258        MavlinkVersion::V1 => {
1259            let mut frame = MAVLinkV1MessageRaw::new();
1260            frame.serialize_message_with_dialect(header, dialect, message)?;
1261            writer.write_all(frame.raw_bytes())?;
1262            Ok(frame.raw_bytes().len())
1263        }
1264        MavlinkVersion::V2 => {
1265            let mut frame = MAVLinkV2MessageRaw::new();
1266            frame.serialize_message_with_dialect(header, dialect, message)?;
1267            writer.write_all(frame.raw_bytes())?;
1268            Ok(frame.raw_bytes().len())
1269        }
1270    }
1271}
1272
1273/// Write a dialect message with MAVLink 2 signing when configured.
1274#[cfg(feature = "mav2-message-signing")]
1275pub fn write_versioned_msg_with_dialect_signed<D: Dialect, W: Write>(
1276    writer: &mut W,
1277    version: MavlinkVersion,
1278    header: MavHeader,
1279    dialect: &D,
1280    message: &D::Message,
1281    signing_data: Option<&SigningData>,
1282) -> Result<usize, MessageWriteError> {
1283    if version == MavlinkVersion::V1 {
1284        return write_versioned_msg_with_dialect(writer, version, header, dialect, message);
1285    }
1286
1287    let mut frame = MAVLinkV2MessageRaw::new();
1288    if let Some(signing_data) = signing_data
1289        && signing_data.config.sign_outgoing
1290    {
1291        frame.serialize_message_with_dialect_for_signing(header, dialect, message)?;
1292        signing_data.sign_message(&mut frame);
1293    } else {
1294        frame.serialize_message_with_dialect(header, dialect, message)?;
1295    }
1296    writer.write_all(frame.raw_bytes())?;
1297    Ok(frame.raw_bytes().len())
1298}
1299
1300/// Write a MAVLink message using the given mavlink version to a [`Write`]r with signing support.
1301///
1302/// When using [`MavlinkVersion::V1`] signing is ignored.
1303///
1304/// # Errors
1305///
1306/// See [`write_` function error documentation](crate#write-errors).
1307#[cfg(feature = "mav2-message-signing")]
1308pub fn write_versioned_msg_signed<M: Message, W: Write>(
1309    w: &mut W,
1310    version: MavlinkVersion,
1311    header: MavHeader,
1312    data: &M,
1313    signing_data: Option<&SigningData>,
1314) -> Result<usize, MessageWriteError> {
1315    match version {
1316        MavlinkVersion::V2 => write_v2_msg_signed(w, header, data, signing_data),
1317        MavlinkVersion::V1 => write_v1_msg(w, header, data),
1318    }
1319}
1320
1321/// Asynchronously write a MAVLink message using the given MAVLink version to a [`AsyncWrite`]r.
1322///
1323/// # Errors
1324///
1325/// See [`write_` function error documentation](crate#write-errors).
1326#[cfg(feature = "tokio")]
1327pub async fn write_versioned_msg_async<M: Message, W: AsyncWrite + Unpin>(
1328    w: &mut W,
1329    version: MavlinkVersion,
1330    header: MavHeader,
1331    data: &M,
1332) -> Result<usize, MessageWriteError> {
1333    match version {
1334        MavlinkVersion::V2 => write_v2_msg_async(w, header, data).await,
1335        MavlinkVersion::V1 => write_v1_msg_async(w, header, data).await,
1336    }
1337}
1338
1339/// Asynchronously write a message through an instance-backed dialect.
1340#[cfg(feature = "tokio")]
1341pub async fn write_versioned_msg_async_with_dialect<D: Dialect, W: AsyncWrite + Unpin>(
1342    writer: &mut W,
1343    version: MavlinkVersion,
1344    header: MavHeader,
1345    dialect: &D,
1346    message: &D::Message,
1347) -> Result<usize, MessageWriteError> {
1348    let mut frame = match version {
1349        MavlinkVersion::V1 => {
1350            let mut frame = MAVLinkV1MessageRaw::new();
1351            frame.serialize_message_with_dialect(header, dialect, message)?;
1352            writer.write_all(frame.raw_bytes()).await?;
1353            return Ok(frame.raw_bytes().len());
1354        }
1355        MavlinkVersion::V2 => MAVLinkV2MessageRaw::new(),
1356    };
1357    frame.serialize_message_with_dialect(header, dialect, message)?;
1358    writer.write_all(frame.raw_bytes()).await?;
1359    Ok(frame.raw_bytes().len())
1360}
1361
1362/// Asynchronously write a dialect message with MAVLink 2 signing when configured.
1363#[cfg(all(feature = "tokio", feature = "mav2-message-signing"))]
1364pub async fn write_versioned_msg_async_with_dialect_signed<D: Dialect, W: AsyncWrite + Unpin>(
1365    writer: &mut W,
1366    version: MavlinkVersion,
1367    header: MavHeader,
1368    dialect: &D,
1369    message: &D::Message,
1370    signing_data: Option<&SigningData>,
1371) -> Result<usize, MessageWriteError> {
1372    if version == MavlinkVersion::V1 {
1373        return write_versioned_msg_async_with_dialect(writer, version, header, dialect, message)
1374            .await;
1375    }
1376    let mut frame = MAVLinkV2MessageRaw::new();
1377    if let Some(signing_data) = signing_data
1378        && signing_data.config.sign_outgoing
1379    {
1380        frame.serialize_message_with_dialect_for_signing(header, dialect, message)?;
1381        signing_data.sign_message(&mut frame);
1382    } else {
1383        frame.serialize_message_with_dialect(header, dialect, message)?;
1384    }
1385    writer.write_all(frame.raw_bytes()).await?;
1386    Ok(frame.raw_bytes().len())
1387}
1388
1389/// Asynchronously write a MAVLink message using the given MAVLink version to a [`AsyncWrite`]r with signing support.
1390///
1391/// When using [`MavlinkVersion::V1`] signing is ignored.
1392///
1393/// # Errors
1394///
1395/// See [`write_` function error documentation](crate#write-errors).
1396#[cfg(all(feature = "tokio", feature = "mav2-message-signing"))]
1397pub async fn write_versioned_msg_async_signed<M: Message, W: AsyncWrite + Unpin>(
1398    w: &mut W,
1399    version: MavlinkVersion,
1400    header: MavHeader,
1401    data: &M,
1402    signing_data: Option<&SigningData>,
1403) -> Result<usize, MessageWriteError> {
1404    match version {
1405        MavlinkVersion::V2 => write_v2_msg_async_signed(w, header, data, signing_data).await,
1406        MavlinkVersion::V1 => write_v1_msg_async(w, header, data).await,
1407    }
1408}
1409
1410/// Asynchronously write a MAVLink message using the given MAVLink version to a [`embedded_io_async::Write`]r.
1411///
1412/// NOTE: it will be add ~70KB to firmware flash size because all *_DATA::ser methods will be add to firmware.
1413/// Use `*_DATA::ser` methods manually to prevent it.
1414#[cfg(all(feature = "embedded", not(feature = "std")))]
1415pub async fn write_versioned_msg_async<M: Message>(
1416    w: &mut impl embedded_io_async::Write,
1417    version: MavlinkVersion,
1418    header: MavHeader,
1419    data: &M,
1420) -> Result<usize, MessageWriteError> {
1421    match version {
1422        MavlinkVersion::V2 => write_v2_msg_async(w, header, data).await,
1423        MavlinkVersion::V1 => write_v1_msg_async(w, header, data).await,
1424    }
1425}
1426
1427/// Write a MAVLink 2 message to a [`Write`]r.
1428///
1429/// # Errors
1430///
1431/// See [`write_` function error documentation](crate#write-errors).
1432pub fn write_v2_msg<M: Message, W: Write>(
1433    w: &mut W,
1434    header: MavHeader,
1435    data: &M,
1436) -> Result<usize, MessageWriteError> {
1437    let mut message_raw = MAVLinkV2MessageRaw::new();
1438    message_raw.serialize_message(header, data);
1439
1440    let header_size = message_raw.header_size();
1441    let payload_length: usize = message_raw.payload_length().into();
1442    let len = consts::STX_SIZE + header_size + payload_length + consts::CHECKSUM_SIZE;
1443
1444    w.write_all(&message_raw.0[..len])?;
1445
1446    Ok(len)
1447}
1448
1449/// Write a MAVLink 2 message to a [`Write`]r with signing support.
1450///
1451/// # Errors
1452///
1453/// See [`write_` function error documentation](crate#write-errors).
1454#[cfg(feature = "mav2-message-signing")]
1455pub fn write_v2_msg_signed<M: Message, W: Write>(
1456    w: &mut W,
1457    header: MavHeader,
1458    data: &M,
1459    signing_data: Option<&SigningData>,
1460) -> Result<usize, MessageWriteError> {
1461    let mut message_raw = MAVLinkV2MessageRaw::new();
1462
1463    let signature_len = if let Some(signing_data) = signing_data
1464        && signing_data.config.sign_outgoing
1465    {
1466        message_raw.serialize_message_for_signing(header, data);
1467        signing_data.sign_message(&mut message_raw);
1468        consts::v2::SIGNATURE_SIZE
1469    } else {
1470        message_raw.serialize_message(header, data);
1471        0
1472    };
1473
1474    let header_size = message_raw.header_size();
1475    let payload_length: usize = message_raw.payload_length().into();
1476    let len =
1477        consts::STX_SIZE + header_size + payload_length + consts::CHECKSUM_SIZE + signature_len;
1478
1479    w.write_all(&message_raw.0[..len])?;
1480
1481    Ok(len)
1482}
1483
1484/// Asynchronously write a MAVLink 2 message to a [`AsyncWrite`]r.
1485///
1486/// # Errors
1487///
1488/// See [`write_` function error documentation](crate#write-errors).
1489#[cfg(feature = "tokio")]
1490pub async fn write_v2_msg_async<M: Message, W: AsyncWrite + Unpin>(
1491    w: &mut W,
1492    header: MavHeader,
1493    data: &M,
1494) -> Result<usize, MessageWriteError> {
1495    let mut message_raw = MAVLinkV2MessageRaw::new();
1496    message_raw.serialize_message(header, data);
1497
1498    let header_size = message_raw.header_size();
1499    let payload_length: usize = message_raw.payload_length().into();
1500    let len = consts::STX_SIZE + header_size + payload_length + consts::CHECKSUM_SIZE;
1501
1502    w.write_all(&message_raw.0[..len]).await?;
1503
1504    Ok(len)
1505}
1506
1507/// Write a MAVLink 2 message to a [`AsyncWrite`]r with signing support.
1508///
1509/// # Errors
1510///
1511/// See [`write_` function error documentation](crate#write-errors).
1512#[cfg(all(feature = "tokio", feature = "mav2-message-signing"))]
1513pub async fn write_v2_msg_async_signed<M: Message, W: AsyncWrite + Unpin>(
1514    w: &mut W,
1515    header: MavHeader,
1516    data: &M,
1517    signing_data: Option<&SigningData>,
1518) -> Result<usize, MessageWriteError> {
1519    let mut message_raw = MAVLinkV2MessageRaw::new();
1520
1521    let signature_len = if let Some(signing_data) = signing_data
1522        && signing_data.config.sign_outgoing
1523    {
1524        message_raw.serialize_message_for_signing(header, data);
1525        signing_data.sign_message(&mut message_raw);
1526        consts::v2::SIGNATURE_SIZE
1527    } else {
1528        message_raw.serialize_message(header, data);
1529        0
1530    };
1531
1532    let header_size = message_raw.header_size();
1533    let payload_length: usize = message_raw.payload_length().into();
1534    let len =
1535        consts::STX_SIZE + header_size + payload_length + consts::CHECKSUM_SIZE + signature_len;
1536
1537    w.write_all(&message_raw.0[..len]).await?;
1538
1539    Ok(len)
1540}
1541
1542/// Asynchronously write a MAVLink 2 message to a [`embedded_io_async::Write`]r.
1543///
1544/// NOTE: it will be add ~70KB to firmware flash size because all *_DATA::ser methods will be add to firmware.
1545/// Use `*_DATA::ser` methods manually to prevent it.
1546///
1547/// # Errors
1548///
1549/// Returns the first error that occurs when writing to the [`embedded_io_async::Write`]r.
1550#[cfg(all(feature = "embedded", not(feature = "std")))]
1551pub async fn write_v2_msg_async<M: Message>(
1552    w: &mut impl embedded_io_async::Write,
1553    header: MavHeader,
1554    data: &M,
1555) -> Result<usize, MessageWriteError> {
1556    let mut message_raw = MAVLinkV2MessageRaw::new();
1557    message_raw.serialize_message(header, data);
1558
1559    let header_size = message_raw.header_size();
1560    let payload_length: usize = message_raw.payload_length().into();
1561    let len = consts::STX_SIZE + header_size + payload_length + consts::CHECKSUM_SIZE;
1562
1563    w.write_all(&message_raw.0[..len])
1564        .await
1565        .map_err(|_| MessageWriteError::Io)?;
1566
1567    Ok(len)
1568}
1569
1570/// Write a MAVLink 1 message to a [`Write`]r.
1571///
1572/// # Errors
1573///
1574/// See [`write_` function error documentation](crate#write-errors).
1575pub fn write_v1_msg<M: Message, W: Write>(
1576    w: &mut W,
1577    header: MavHeader,
1578    data: &M,
1579) -> Result<usize, MessageWriteError> {
1580    if data.message_id() > u8::MAX.into() {
1581        return Err(MessageWriteError::MAVLink2Only);
1582    }
1583    let mut message_raw = MAVLinkV1MessageRaw::new();
1584    message_raw.serialize_message(header, data);
1585
1586    let payload_length: usize = message_raw.payload_length().into();
1587    let len = consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length + consts::CHECKSUM_SIZE;
1588
1589    w.write_all(&message_raw.0[..len])?;
1590
1591    Ok(len)
1592}
1593
1594/// Asynchronously write a MAVLink 1 message to a [`AsyncWrite`]r.
1595///
1596/// # Errors
1597///
1598/// Returns the first error that occurs when writing to the [`AsyncWrite`]r.
1599#[cfg(feature = "tokio")]
1600pub async fn write_v1_msg_async<M: Message, W: AsyncWrite + Unpin>(
1601    w: &mut W,
1602    header: MavHeader,
1603    data: &M,
1604) -> Result<usize, MessageWriteError> {
1605    if data.message_id() > u8::MAX.into() {
1606        return Err(MessageWriteError::MAVLink2Only);
1607    }
1608    let mut message_raw = MAVLinkV1MessageRaw::new();
1609    message_raw.serialize_message(header, data);
1610
1611    let payload_length: usize = message_raw.payload_length().into();
1612    let len = consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length + consts::CHECKSUM_SIZE;
1613
1614    w.write_all(&message_raw.0[..len]).await?;
1615
1616    Ok(len)
1617}
1618
1619/// Write a MAVLink 1 message to a [`embedded_io_async::Write`]r.
1620///
1621/// NOTE: it will be add ~70KB to firmware flash size because all *_DATA::ser methods will be add to firmware.
1622/// Use `*_DATA::ser` methods manually to prevent it.
1623#[cfg(all(feature = "embedded", not(feature = "std")))]
1624pub async fn write_v1_msg_async<M: Message>(
1625    w: &mut impl embedded_io_async::Write,
1626    header: MavHeader,
1627    data: &M,
1628) -> Result<usize, MessageWriteError> {
1629    if data.message_id() > u8::MAX.into() {
1630        return Err(MessageWriteError::MAVLink2Only);
1631    }
1632    let mut message_raw = MAVLinkV1MessageRaw::new();
1633    message_raw.serialize_message(header, data);
1634
1635    let payload_length: usize = message_raw.payload_length().into();
1636    let len = consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length + consts::CHECKSUM_SIZE;
1637
1638    w.write_all(&message_raw.0[..len])
1639        .await
1640        .map_err(|_| MessageWriteError::Io)?;
1641
1642    Ok(len)
1643}