mavspec 0.1.0-alpha6

A set of code generation utilities for MAVLink protocol.
Documentation

MAVSpec

A code-generator for MAVLink.

repository crates.io API docs issues

MAVLink is a lightweight open protocol for communicating between drones, onboard components and ground control stations. It is used by such autopilots like PX4 or ArduPilot. MAVLink has simple and compact serialization model. The basic abstraction is message which can be sent through the link (UDP, TCP, UNIX socket, UART, whatever) and deserialized into a struct with fields of primitive types or arrays of primitive types. Such fields can be additionally restricted by enum variants, annotated with metadata like units of measurements, default or invalid values. There are several MAVLink dialects. Official dialect definitions are XML files that can be found in the MAVlink repository. Based on message abstractions, MAVLink defines so-called microservices that specify how clients should respond on a particular message under certain conditions or how they should initiate a particular action.

This library is a building block for other MAVLink-related tools (telemetry collectors, IO, etc.). It is only responsible for code generation. Other Mavka projects are focused on different areas:

  • MAVInspect responsible for parsing mavlink message XML definitions. MAVSpec is using this it to discover and parse MAVLink dialects.
  • Mavio, a minimalistic library for transport-agnostic MAVLink communication written in Rust. It supports no-std (and no-alloc) targets and focuses on stateless parts of MAVLink protocol.
  • Maviola (WIP), an elaborated MAVLink communication library based on Mavio that takes care about stateful features: sequencing, message time-stamping, automatic heartbeats, simplifies message signing, and so on.

This project respects semantic versioning.

Install

Install as a Cargo dependency.

cargo add --build mavspec

Since you probably want to generate code as a part of you build sequence, we suggest to also add MAVSpec as a build dependency.

cargo add --build mavspec

Usage

The following explains how to use library API, for command-line tool usage check CLI section.

Rust

API documentation for Rust code-generation can be found here.

Add MAVSpec with rust feature to your Cargo.toml.

[dependencies]
#...
mavspec = { version = "0.1.0", features = ["rust"] }
#...

This feature enables interfaces upon which your generated code will depend. You can access these interfaces through use mavspec::rust::spec.

Optionally enable std (for Rust standard library) or alloc (for memory allocation support) features if your target supports them (if you are not developing for an embedded devices, then we suggest to always enable std).

Add MAVSpec with rust_gen as a build dependency:

[build-dependencies]
#...
mavspec = { version = "0.1.0", features = ["rust_gen"] }
#...

If necessary, add optional section to your Cargo.toml to generate only specific messages:

[package.metadata.mavspec]
messages = ["HEARTBEAT", "PROTOCOL_VERSION", "MAV_INSPECT_V1", "COMMAND_INT", "COMMAND_LONG"]
all_enums = false
generate_tests = false

This will greatly reduce compile time and may slightly reduce memory footprint (if you are not going to expose autogenerated code as a part of your library API, then Rust compiler will probably optimize away all unused pieces).

The all_enum key controls which enums will be generated. By default, only MAVLink enums required for selected messages will be generated. Set all_enums = true to generate all enums. If messages key is not specified, then all_enums won't have any effect.

If you want to generate tests for generated code, set generate_tests to true. This mode is disabled by default.

Update your build.rs:

use std::env::var;
use std::path::Path;

use mavspec::rust::BuildHelper;

fn main() {
    // Assume that your library and `message_definitions` are both in the root of your project.
    let sources = vec![
        "./message_definitions/standard",
        "./message_definitions/extra",
    ];
    // Output path
    let destination = Path::new(&var("OUT_DIR").unwrap()).join("mavlink");
    // Path to your `Cargo.toml` manifest
    let manifest_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("Cargo.toml");

    // Parse XML definitions and generate Rust code
    BuildHelper::builder(&destination)
        .set_sources(&sources)
        .set_manifest_path(&manifest_path)
        .generate()
        .unwrap();
}

The OUT_DIR environment variable is provided by Rust build toolchain and points to output library for your crate. It is considered a bad practice to write outside this path in the build scripts.

Finally, import generated code in your lib.rs (or anywhere it seems appropriate):

mod mavlink {
    include!(concat!(env!("OUT_DIR"), "/mavlink/mod.rs"));
}
pub use mavlink::dialects;

Check examples/rust for a slightly more elaborated example which uses Cargo features as flags for MAVLink dialect selection.

CLI

Parse XML definitions from ./message_definitions/standard and generate dialects in tmp/mavlink directory:

cargo run --bin mavspec -- --src message_definitions/standard --out tmp/mavlink rust

Print mavspec help for Rust code generator:

cargo run --bin mavspec -- rust -h

Examples

  • examples/rust — an example library with autogenerated code.
    cargo run --package mavspec_examples_rust --bin mavspec_examples_rust
    

License

Here we simply comply with the suggested dual licensing according to Rust API Guidelines (C-PERMISSIVE).

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.