From 63b6af9fe59eb5cdea6aa385d0c3f4f3d2f2303b Mon Sep 17 00:00:00 2001 From: pv42 Date: Wed, 31 Jul 2024 09:40:30 +0200 Subject: [PATCH 01/12] fix: Enum entries without value are generated as isize --- mavlink-bindgen/src/parser.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mavlink-bindgen/src/parser.rs b/mavlink-bindgen/src/parser.rs index 1ed238b815..e6ee66f3ab 100644 --- a/mavlink-bindgen/src/parser.rs +++ b/mavlink-bindgen/src/parser.rs @@ -308,7 +308,7 @@ impl MavEnum { } fn emit_defs(&self) -> Vec { - let mut cnt = 0isize; + let mut cnt = 0u32; self.entries .iter() .map(|enum_entry| { @@ -330,7 +330,7 @@ impl MavEnum { value = quote!(#cnt); } else { let tmp_value = enum_entry.value.unwrap(); - cnt = cnt.max(tmp_value as isize); + cnt = cnt.max(tmp_value as u32); let tmp = TokenStream::from_str(&tmp_value.to_string()).unwrap(); value = quote!(#tmp); }; From 9e9ef0929c07d20d2681dac6ecced58335bf1895 Mon Sep 17 00:00:00 2001 From: pv42 Date: Tue, 6 Aug 2024 18:33:57 +0200 Subject: [PATCH 02/12] Feat: add cli argument documentation --- mavlink-bindgen/src/cli.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mavlink-bindgen/src/cli.rs b/mavlink-bindgen/src/cli.rs index 30fee8a843..5ff37b26f1 100644 --- a/mavlink-bindgen/src/cli.rs +++ b/mavlink-bindgen/src/cli.rs @@ -4,11 +4,16 @@ use clap::Parser; use mavlink_bindgen::{emit_cargo_build_messages, format_generated_code, generate, BindGenError}; #[derive(Parser)] +/// Generate Rust bindings from MAVLink message dialect XML files. struct Cli { + /// Path to the directory containing the MAVLink dialect definitions. definitions_dir: PathBuf, + /// Path to the directory where the code is generated into, must already exist. destination_dir: PathBuf, + /// format code generated code #[arg(long)] format_generated_code: bool, + /// prints cargo build messages indicating when the code has to be rebuild #[arg(long)] emit_cargo_build_messages: bool, } From d788b66f3637028513a40ef69c0a7fc20fe9af94 Mon Sep 17 00:00:00 2001 From: pv42 Date: Tue, 6 Aug 2024 18:34:27 +0200 Subject: [PATCH 03/12] Feat: add mavlink-bindgen readme --- mavlink-bindgen/README.md | 94 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 mavlink-bindgen/README.md diff --git a/mavlink-bindgen/README.md b/mavlink-bindgen/README.md new file mode 100644 index 0000000000..7e678a70cb --- /dev/null +++ b/mavlink-bindgen/README.md @@ -0,0 +1,94 @@ +# rust-mavlink + +[![Build status](https://github.com/mavlink/rust-mavlink/actions/workflows/test.yml/badge.svg)](https://github.com/mavlink/rust-mavlink/actions/workflows/test.yml) + +[![Crate info](https://img.shields.io/crates/v/mavlink-bindgen.svg)](https://crates.io/crates/mavlink-bindgen) +[![Documentation](https://docs.rs/mavlink-bindgen/badge.svg)](https://docs.rs/mavlink-bindgen) + +Library and CLI for code generator of the Rust implementation of the [MAVLink](https://mavlink.io/en) UAV messaging protocol. + +`mavlink-bindgen` can be used to create MAVLink bindings for Rust. This is used from `build.rs` in the [mavlink](https://crates.io/crates/mavlink) crate to create bindings from the standard MAVLink dialects in https://github.com/mavlink/mavlink. + +## Usage + +`mavlink-bindgen` can be used as a code generator from `build.rs` as done is the `mavlink` crate for a custom MAVLink dialect or as a CLI tool to generate rust binding from XML dialect definitions. The generated code will depend on the [mavlink-core](https://crates.io/crates/mavlink-core) crate in both use cases. + +### CLI + +Build using cargo with `cli` feature enabled: + +```shell +cargo build --features cli +``` + +Alternatively you can build and install `mavlink-bindgen` to you locally installed crates: + +```shell +cargo install mavlink-bindgen --features cli +``` + +Generate code using the resulting binary: + +```shell +mavlink-bindgen --format-generated-code message_definitions mavlink_dialects +``` + +The full options are shown below. + +```shell +Usage: mavlink-bindgen [OPTIONS] + +Arguments: + Path to the directory containing the MAVLink dialect definitions + Path to the directory where the code is generated into + +Options: + --format-generated-code format code generated code + --emit-cargo-build-messages prints cargo build message indicating when the code has to be rebuild + -h, --help Print help +``` + +### Library as build dependency + +Add to your Cargo.toml: + +```toml +mavlink-bindgen = "0.13.1" +``` + +Add a `build/main.rs` or `build.rs` to your project if it does not already exist. Then add the following to the `main` function to generate the code: + +```rs +let out_dir = env::var("OUT_DIR").unwrap(); +let result = match mavlink_bindgen::generate(definitions_dir, out_dir) { + Ok(r) => r, + Err(e) => { + eprintln!("{e}"); + return ExitCode::FAILURE; + } +}; +``` + +If the generated code should be formated use + +```rs + mavlink_bindgen::format_generated_code(&result); +``` + +To tell cargo when to regenerate code from the definitions use: + +```rs + mavlink_bindgen::emit_cargo_build_messages(&result); +``` + +Finally include the generated code into the `lib.rs` or `main.rs` : + +```rs +#![cfg_attr(not(feature = "std"), no_std)] +// include generate definitions +include!(concat!(env!("OUT_DIR"), "/mod.rs")); + +pub use mavlink_core::*; +``` + +This approach is used by the mavlink crate see its build script for an example. From 69703a7892f773a197d20a3ea3fec365e1f83c59 Mon Sep 17 00:00:00 2001 From: pv42 Date: Wed, 7 Aug 2024 14:08:36 +0200 Subject: [PATCH 04/12] Fix: CouldNotReadDefinitionFile doc is copy pasted --- mavlink-bindgen/src/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mavlink-bindgen/src/error.rs b/mavlink-bindgen/src/error.rs index 957ec5506d..fe32023389 100644 --- a/mavlink-bindgen/src/error.rs +++ b/mavlink-bindgen/src/error.rs @@ -8,7 +8,7 @@ pub enum BindGenError { source: std::io::Error, path: std::path::PathBuf, }, - /// Represents a failure to read the MAVLink definitions directory. + /// Represents a failure to read a MAVLink definition file. #[error("Could not read definition file {path}: {source}")] CouldNotReadDefinitionFile { source: std::io::Error, From 47058869f0030310c70031573f5a50809c717f3d Mon Sep 17 00:00:00 2001 From: pv42 Date: Wed, 7 Aug 2024 14:22:49 +0200 Subject: [PATCH 05/12] Feat: add doc to public functions in mavlink-bindgen's lib.rs --- mavlink-bindgen/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mavlink-bindgen/src/lib.rs b/mavlink-bindgen/src/lib.rs index 3448fe88da..22f1efa2ce 100644 --- a/mavlink-bindgen/src/lib.rs +++ b/mavlink-bindgen/src/lib.rs @@ -23,6 +23,9 @@ pub struct GeneratedBindings { pub mod_rs: PathBuf, } +/// Generate Rust MAVLink dialect binding for dialects present in `definitions_dir` into `destination_dir`. +/// +/// If successful returns paths of generated bindings linked to their dialect definitions files. pub fn generate, P2: AsRef>( definitions_dir: P1, destination_dir: P2, @@ -99,6 +102,7 @@ fn _generate( } } +/// Formats generated code using `rustfmt`. pub fn format_generated_code(result: &GeneratedBindings) { if let Err(error) = Command::new("rustfmt") .args( @@ -114,6 +118,7 @@ pub fn format_generated_code(result: &GeneratedBindings) { } } +/// Prints definitions for cargo that describe which files the generated code depends on, indicating when it has to be regenerated. pub fn emit_cargo_build_messages(result: &GeneratedBindings) { for binding in &result.bindings { // Re-run build if definition file changes From d16896264308d96a34632be46a7bcfb39c43091a Mon Sep 17 00:00:00 2001 From: pv42 Date: Wed, 7 Aug 2024 14:25:42 +0200 Subject: [PATCH 06/12] Fix: improve mavlink-bindgen readme --- mavlink-bindgen/README.md | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/mavlink-bindgen/README.md b/mavlink-bindgen/README.md index 7e678a70cb..c813b5fc3a 100644 --- a/mavlink-bindgen/README.md +++ b/mavlink-bindgen/README.md @@ -1,46 +1,46 @@ -# rust-mavlink +# mavlink-bindgen [![Build status](https://github.com/mavlink/rust-mavlink/actions/workflows/test.yml/badge.svg)](https://github.com/mavlink/rust-mavlink/actions/workflows/test.yml) - [![Crate info](https://img.shields.io/crates/v/mavlink-bindgen.svg)](https://crates.io/crates/mavlink-bindgen) [![Documentation](https://docs.rs/mavlink-bindgen/badge.svg)](https://docs.rs/mavlink-bindgen) -Library and CLI for code generator of the Rust implementation of the [MAVLink](https://mavlink.io/en) UAV messaging protocol. +Library and CLI for generating code for the Rust implementation of the [MAVLink](https://mavlink.io/en) UAV messaging protocol. -`mavlink-bindgen` can be used to create MAVLink bindings for Rust. This is used from `build.rs` in the [mavlink](https://crates.io/crates/mavlink) crate to create bindings from the standard MAVLink dialects in https://github.com/mavlink/mavlink. +`mavlink-bindgen` can be used to create MAVLink bindings for Rust. This is used from `build.rs` in the [mavlink](https://crates.io/crates/mavlink) crate to create bindings from the standard MAVLink dialects in . ## Usage -`mavlink-bindgen` can be used as a code generator from `build.rs` as done is the `mavlink` crate for a custom MAVLink dialect or as a CLI tool to generate rust binding from XML dialect definitions. The generated code will depend on the [mavlink-core](https://crates.io/crates/mavlink-core) crate in both use cases. +`mavlink-bindgen` can be used as a code generator from `build.rs` as done is the `mavlink` crate for a custom MAVLink dialect or as a CLI tool to generate rust binding from XML dialect definitions. The generated code will depend on the [mavlink-core](https://crates.io/crates/mavlink-core) crate in both use cases. Each dialect generated will be locked behind a feature flag of the same name, that must be enabled when using the generated code. ### CLI -Build using cargo with `cli` feature enabled: +Build the binary using cargo with `cli` feature enabled: ```shell +cd mavlink-bindgen cargo build --features cli ``` -Alternatively you can build and install `mavlink-bindgen` to you locally installed crates: +Alternatively you can build and install `mavlink-bindgen` to your locally installed crates: ```shell cargo install mavlink-bindgen --features cli ``` -Generate code using the resulting binary: +To generate code using the resulting binary: ```shell mavlink-bindgen --format-generated-code message_definitions mavlink_dialects ``` -The full options are shown below. +The full command line options are shown below. ```shell Usage: mavlink-bindgen [OPTIONS] Arguments: Path to the directory containing the MAVLink dialect definitions - Path to the directory where the code is generated into + Path to the directory where the code is generated into, must already exist Options: --format-generated-code format code generated code @@ -48,6 +48,8 @@ Options: -h, --help Print help ``` +The output dir will contain a `mod.rs` file with each dialect in its own file locked behind a feature flag. + ### Library as build dependency Add to your Cargo.toml: @@ -91,4 +93,6 @@ include!(concat!(env!("OUT_DIR"), "/mod.rs")); pub use mavlink_core::*; ``` -This approach is used by the mavlink crate see its build script for an example. +Since each dialect is locked behind a feature flag these need to be enabled for the dialects to become available when using the generated code. + +This approach is used by the `mavlink` crate see its build script for an example. From a5edd295a491e43d857dbdc8d2371cf9321c6738 Mon Sep 17 00:00:00 2001 From: pv42 Date: Wed, 7 Aug 2024 14:39:29 +0200 Subject: [PATCH 07/12] Fix: set cargo toml link to readme in mavlink-bindgen, add missing repo link to mavlink-bindgen --- mavlink-bindgen/Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mavlink-bindgen/Cargo.toml b/mavlink-bindgen/Cargo.toml index 0a5ee88567..32403c9a2f 100644 --- a/mavlink-bindgen/Cargo.toml +++ b/mavlink-bindgen/Cargo.toml @@ -4,7 +4,8 @@ version = "0.13.1" edition = "2021" license = "MIT/Apache-2.0" description = "Library used by rust-mavlink." -readme = "../README.md" +readme = "README.md" +repository = "https://github.com/mavlink/rust-mavlink" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From c93fd578d7bca76a0644b49b22622e9774c7ab15 Mon Sep 17 00:00:00 2001 From: pv42 Date: Thu, 8 Aug 2024 13:54:03 +0200 Subject: [PATCH 08/12] fix: incompatibility flags of v2 messages are checked --- mavlink-core/src/lib.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/mavlink-core/src/lib.rs b/mavlink-core/src/lib.rs index ef18bfa994..eac97934b7 100644 --- a/mavlink-core/src/lib.rs +++ b/mavlink-core/src/lib.rs @@ -490,6 +490,7 @@ pub async fn read_v1_msg_async( } const MAVLINK_IFLAG_SIGNED: u8 = 0x01; +const MAVLINK_SUPPORTED_IFLAGS: u8 = MAVLINK_IFLAG_SIGNED; #[derive(Debug, Copy, Clone, PartialEq, Eq)] // Follow protocol definition: `` @@ -677,6 +678,12 @@ pub fn read_v2_raw_message( let header = &reader.peek_exact(MAVLinkV2MessageRaw::HEADER_SIZE)? [..MAVLinkV2MessageRaw::HEADER_SIZE]; message.mut_header().copy_from_slice(header); + + if message.incompatibility_flags() & !MAVLINK_SUPPORTED_IFLAGS > 0 { + // if there are incompatibility flags set that we do not know discard the message + continue; + } + let packet_length = message.raw_bytes().len() - 1; let payload_and_checksum_and_sign = &reader.peek_exact(packet_length)?[MAVLinkV2MessageRaw::HEADER_SIZE..packet_length]; @@ -720,6 +727,12 @@ pub async fn read_v2_raw_message_async( .read_exact(message.mut_header()) .await .map_err(|_| error::MessageReadError::Io)?; + + if message.incompatibility_flags() & !MAVLINK_SUPPORTED_IFLAGS > 0 { + // if there are incompatibility flags set that we do not know discard the message + continue; + } + reader .read_exact(message.mut_payload_and_checksum_and_sign()) .await From c10d36e9a4fff04588d87f2435a6cd515393fbfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Ant=C3=B4nio=20Cardoso?= Date: Thu, 22 Aug 2024 11:24:36 -0300 Subject: [PATCH 09/12] mavlink-core: src: read shouldn't be mut --- mavlink-core/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mavlink-core/src/lib.rs b/mavlink-core/src/lib.rs index eac97934b7..3be0db377a 100644 --- a/mavlink-core/src/lib.rs +++ b/mavlink-core/src/lib.rs @@ -511,7 +511,7 @@ impl MAVLinkV2MessageRaw { } #[inline] - pub fn header(&mut self) -> &[u8] { + pub fn header(&self) -> &[u8] { &self.0[1..=Self::HEADER_SIZE] } From 5a1cd30fd9f9e9f002ddf7968be60f0be229d4b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Ant=C3=B4nio=20Cardoso?= Date: Thu, 22 Aug 2024 11:27:27 -0300 Subject: [PATCH 10/12] cargo: Add tokio-1 feature for tokio-based async-io --- mavlink-core/Cargo.toml | 2 ++ mavlink/Cargo.toml | 1 + 2 files changed, 3 insertions(+) diff --git a/mavlink-core/Cargo.toml b/mavlink-core/Cargo.toml index e4df6bbab6..db3504e1cc 100644 --- a/mavlink-core/Cargo.toml +++ b/mavlink-core/Cargo.toml @@ -26,6 +26,7 @@ embedded-io-async = { version = "0.6.1", optional = true } serde = { version = "1.0.115", optional = true, features = ["derive"] } serde_arrays = { version = "0.1.0", optional = true } serial = { version = "0.4", optional = true } +tokio = { version = "1.0", default-features = false, features = ["io-util"], optional = true } [features] "std" = ["byteorder/std"] @@ -38,4 +39,5 @@ serial = { version = "0.4", optional = true } "embedded" = ["dep:embedded-io", "dep:embedded-io-async"] "embedded-hal-02" = ["dep:nb", "dep:embedded-hal-02"] "serde" = ["dep:serde", "dep:serde_arrays"] +"tokio-1" = ["dep:tokio"] default = ["std", "tcp", "udp", "direct-serial", "serde"] diff --git a/mavlink/Cargo.toml b/mavlink/Cargo.toml index 4fd09da02d..714d79cd52 100644 --- a/mavlink/Cargo.toml +++ b/mavlink/Cargo.toml @@ -99,6 +99,7 @@ serde_arrays = { version = "0.1.0", optional = true } "embedded" = ["mavlink-core/embedded"] "embedded-hal-02" = ["mavlink-core/embedded-hal-02"] "serde" = ["mavlink-core/serde", "dep:serde", "dep:serde_arrays"] +"tokio-1" = ["mavlink-core/tokio-1"] default = ["std", "tcp", "udp", "direct-serial", "serde", "ardupilotmega"] # build with all features on docs.rs so that users viewing documentation From 12b1e2b8afa63eb78dfea45d9878287043b8d8ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Ant=C3=B4nio=20Cardoso?= Date: Thu, 22 Aug 2024 11:28:04 -0300 Subject: [PATCH 11/12] mavlink-core: src: Add tokio-based async read/write --- mavlink-core/src/lib.rs | 106 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/mavlink-core/src/lib.rs b/mavlink-core/src/lib.rs index 3be0db377a..342d7297b9 100644 --- a/mavlink-core/src/lib.rs +++ b/mavlink-core/src/lib.rs @@ -698,6 +698,41 @@ pub fn read_v2_raw_message( } } +/// Async read a raw buffer with the mavlink message +/// V2 maximum size is 280 bytes: `` +#[cfg(feature = "tokio-1")] +pub async fn read_v2_raw_message_async( + reader: &mut R, +) -> Result { + loop { + loop { + // search for the magic framing value indicating start of mavlink message + if reader.read_u8().await? == MAV_STX_V2 { + break; + } + } + + let mut message = MAVLinkV2MessageRaw::new(); + + message.0[0] = MAV_STX_V2; + let header_len = reader.read_exact(message.mut_header()).await?; + assert_eq!(header_len, MAVLinkV2MessageRaw::HEADER_SIZE); + + if message.incompatibility_flags() & !MAVLINK_SUPPORTED_IFLAGS > 0 { + // if there are incompatibility flags set that we do not know discard the message + continue; + } + + reader + .read_exact(message.mut_payload_and_checksum_and_sign()) + .await?; + + if message.has_valid_crc::() { + return Ok(message); + } + } +} + /// Async read a raw buffer with the mavlink message /// V2 maximum size is 280 bytes: `` /// @@ -766,6 +801,27 @@ pub fn read_v2_msg( .map_err(|err| err.into()) } +/// Async read a MAVLink v2 message from a Read stream. +#[cfg(feature = "tokio-1")] +pub async fn read_v2_msg_async( + read: &mut R, +) -> Result<(MavHeader, M), error::MessageReadError> { + let message = read_v2_raw_message_async::(read).await?; + + M::parse(MavlinkVersion::V2, message.message_id(), message.payload()) + .map(|msg| { + ( + MavHeader { + sequence: message.sequence(), + system_id: message.system_id(), + component_id: message.component_id(), + }, + msg, + ) + }) + .map_err(|err| err.into()) +} + /// Async read a MAVLink v2 message from a Read stream. /// /// NOTE: it will be add ~80KB to firmware flash size because all *_DATA::deser methods will be add to firmware. @@ -807,6 +863,20 @@ pub fn write_versioned_msg( } } +/// Async write a message using the given mavlink version +#[cfg(feature = "tokio-1")] +pub async fn write_versioned_msg_async( + w: &mut W, + version: MavlinkVersion, + header: MavHeader, + data: &M, +) -> Result { + match version { + MavlinkVersion::V2 => write_v2_msg_async(w, header, data).await, + MavlinkVersion::V1 => write_v1_msg_async(w, header, data).await, + } +} + /// Async write a message using the given mavlink version /// /// NOTE: it will be add ~70KB to firmware flash size because all *_DATA::ser methods will be add to firmware. @@ -841,6 +911,24 @@ pub fn write_v2_msg( Ok(len) } +/// Async write a MAVLink v2 message to a Write stream. +#[cfg(feature = "tokio-1")] +pub async fn write_v2_msg_async( + w: &mut W, + header: MavHeader, + data: &M, +) -> Result { + let mut message_raw = MAVLinkV2MessageRaw::new(); + message_raw.serialize_message(header, data); + + let payload_length: usize = message_raw.payload_length().into(); + let len = 1 + MAVLinkV2MessageRaw::HEADER_SIZE + payload_length + 2; + + w.write_all(&message_raw.0[..len]).await?; + + Ok(len) +} + /// Async write a MAVLink v2 message to a Write stream. /// /// NOTE: it will be add ~70KB to firmware flash size because all *_DATA::ser methods will be add to firmware. @@ -881,6 +969,24 @@ pub fn write_v1_msg( Ok(len) } +/// Async write a MAVLink v1 message to a Write stream. +#[cfg(feature = "tokio-1")] +pub async fn write_v1_msg_async( + w: &mut W, + header: MavHeader, + data: &M, +) -> Result { + let mut message_raw = MAVLinkV1MessageRaw::new(); + message_raw.serialize_message(header, data); + + let payload_length: usize = message_raw.payload_length().into(); + let len = 1 + MAVLinkV1MessageRaw::HEADER_SIZE + payload_length + 2; + + w.write_all(&message_raw.0[..len]).await?; + + Ok(len) +} + /// Write a MAVLink v1 message to a Write stream. /// /// NOTE: it will be add ~70KB to firmware flash size because all *_DATA::ser methods will be add to firmware. From 311cd65d429d8dfe8400b41d76bd46b4a70be3fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Ant=C3=B4nio=20Cardoso?= Date: Thu, 22 Aug 2024 11:40:46 -0300 Subject: [PATCH 12/12] Revert "Use versions over path" This reverts commit 2e40341e6d1a0f76568defb39e334e8f883a3a12. --- mavlink/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mavlink/Cargo.toml b/mavlink/Cargo.toml index 714d79cd52..476c80b0b8 100644 --- a/mavlink/Cargo.toml +++ b/mavlink/Cargo.toml @@ -19,7 +19,7 @@ edition = "2018" rust-version = "1.65.0" [build-dependencies] -mavlink-bindgen = { version = "0.13.1", default-features = false } +mavlink-bindgen = { path = "../mavlink-bindgen", default-features = false } [[example]] name = "mavlink-dump" @@ -27,7 +27,7 @@ path = "examples/mavlink-dump/src/main.rs" required-features = ["ardupilotmega"] [dependencies] -mavlink-core = { version = "0.13.1", default-features = false } +mavlink-core = { path = "../mavlink-core", default-features = false } num-traits = { workspace = true, default-features = false } num-derive = { workspace = true } bitflags = { workspace = true }