MAVSpec
=======
A code-generator for [MAVLink](https://mavlink.io/en/).
[`repository`](https://gitlab.com/mavka/libs/mavspec)
[`crates.io`](https://crates.io/crates/mavspec)
[`API docs`](https://docs.rs/mavspec/latest/mavspec/)
[`issues`](https://gitlab.com/mavka/libs/mavspec/-/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](https://px4.io) or [ArduPilot](https://ardupilot.org/#). 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](https://mavlink.io/en/guide/xml_schema.html) that can be found in the MAVlink
[repository](https://github.com/mavlink/mavlink/tree/master/message_definitions/v1.0). Based on `message` abstractions,
MAVLink defines so-called [`microservices`](https://mavlink.io/en/services/) 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](https://mavka.gitlab.io/home/) projects are focused on different areas:
* [MAVInspect](https://gitlab.com/mavka/libs/mavspec) responsible for parsing mavlink message XML definitions. MAVSpec
is using this it to discover and parse MAVLink dialects.
* [Mavio](https://gitlab.com/mavka/libs/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](https://gitlab.com/mavka/libs/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`](https://semver.org).
Install
-------
Install as a Cargo dependency.
```shell
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.
```shell
cargo add --build mavspec
```
Usage
-----
> The following explains how to use library API, for command-line tool usage check [CLI](#cli) section.
### Rust
API documentation for Rust code-generation can be found [here](https://docs.rs/mavspec/latest/mavspec/rust).
Add MAVSpec with `rust` feature to your `Cargo.toml`.
```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:
```toml
[build-dependencies]
#...
mavspec = { version = "0.1.0", features = ["rust_gen"] }
#...
```
If necessary, add optional section to your `Cargo.toml` to generate only specific messages:
```toml
[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`:
```rust
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):
```rust
mod mavlink {
include!(concat!(env!("OUT_DIR"), "/mavlink/mod.rs"));
}
pub use mavlink::dialects;
```
Check [`examples/rust`](examples/rust/README.md) for a slightly more elaborated example which uses Cargo features as flags for MAVLink
dialect selection.
CLI
---
Parse XML definitions from [`./message_definitions/standard`](./message_definitions/standard) and generate dialects in
`tmp/mavlink` directory:
```shell
cargo run --bin mavspec -- --src message_definitions/standard --out tmp/mavlink rust
```
Print `mavspec` help for Rust code generator:
```shell
cargo run --bin mavspec -- rust -h
```
Examples
--------
- [`examples/rust`](examples/rust/README.md) — an example library with autogenerated code.
```shell
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](https://rust-lang.github.io/api-guidelines/about.html) (C-PERMISSIVE).
Licensed under either of
* Apache License, Version 2.0
([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license
([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
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.