px4/
lib.rs

1//! # PX4 bindings for Rust
2//!
3//! This crate provides the framework to make dynamically loadable PX4 modules
4//! in Rust. Right now, it provides bindings for the two most important APIs:
5//! Logging and uORB. It also provides the entry point for your module, and
6//! handles panics on the main thread of the module.
7//!
8//! See the
9//! [`example` directory](https://github.com/dronesforwork/px4-rust/tree/master/example)
10//! for an example module.
11//!
12//! ## Compiling and running
13//!
14//! To build a PX4 module in Rust, create a crate as you would for any other
15//! application binary, and then add the following to your Cargo.toml:
16//!
17//! ```text
18//! [lib]
19//! crate-type = ["cdylib"]
20//! path = "src/module.rs"
21//! ```
22//!
23//! This will turn your program into a loadable module instead of a standalone
24//! application. The resulting file will be called `lib<name>.so`, which you
25//! can manually rename to `<name>.px4mod` if you want.
26//!
27//! To run your module, use the
28//! [`dyn`](https://dev.px4.io/en/middleware/modules_command.html#dyn)
29//! PX4 command. Give it the full path name, followed by any arguments to your
30//! module. Note that `dyn` will *not* reload your file if you run it again.
31//! If you want to run a changed version of your module, you'll either need to
32//! restart PX4, or move/rename the file.
33//!
34//! ## Entry point
35//!
36//! Mark your entry function with `#[px4_module_main]`. The boilerplate code
37//! needed to set up the environment and export the function under the right
38//! name is then inserted automatically.
39//!
40//! Your main function should take a `&[&str]` as argument. It *may* return a
41//! `i32` status code, either directly, or as the error type of a `Result`.  A
42//! panic from your main thread is caught and results in a status code of −1.
43//!
44//! ### Example
45//!
46//! ```
47//! use px4::px4_module_main;
48//!
49//! #[px4_module_main]
50//! fn my_module(args: &[&str]) -> i32 {
51//!   0
52//! }
53//! ```
54//!
55//! ## Logging
56//!
57//! As soon as your main function is entered, logging is already set up using
58//! the standard [`log` crate](https://docs.rs/log/). You can use the standard
59//! logging macros such as `info!`, `warn!`, and `error!` to log messages,
60//! equivalent to `PX4_INFO` (etc.) in C and C++.
61//!
62//! Use the `info_raw!` macro to send raw output, equivalent to the
63//! `PX4_INFO_RAW` macro in C and C++.
64//! Do not use standard output or standard error for this, as the standard
65//! streams of the PX4 process are often not the ones connected to the terminal
66//! the user is looking at.
67//!
68//! ### Example
69//!
70//! ```
71//! use log::{info, warn};
72//! use px4::px4_module_main;
73//!
74//! #[px4_module_main]
75//! fn my_module(args: &[&str]) {
76//!   info!("Hello World!");
77//!   warn!("A warning!");
78//!   panic!("Bye!");
79//! }
80//! ```
81//!
82//! ## uORB
83//!
84//! Message definitions can be imported from `.msg` files, and then subscribed
85//! to or published. See the [`uorb` module](uorb/index.html) for documentation
86//! on how to use the uORB bindings.
87//!
88//! ### Example
89//!
90//! ```
91//! use log::info;
92//! use px4::{px4_module_main, px4_message};
93//! use px4::uorb::{Publish, Subscribe};
94//!
95//! #[px4_message("../example/msg/debug_value.msg")]
96//! pub struct debug_value;
97//!
98//! #[px4_module_main]
99//! fn my_module(args: &[&str]) {
100//!
101//!   let mut publ = debug_value::advertise();
102//!   publ.publish(&debug_value { timestamp: 0, value: 1.0, ind: 3 }).unwrap();
103//!
104//!   let sub = debug_value::subscribe().unwrap();
105//!   info!("Latest debug message: {:?}", sub.get().unwrap());
106//! }
107//! ```
108
109use std::ffi::CStr;
110use std::os::raw::c_char;
111
112pub mod uorb;
113mod logging;
114
115pub use crate::logging::{log_raw, LogLevel};
116pub use px4_macros::{px4_message, px4_module_main};
117
118#[doc(hidden)]
119pub unsafe fn _run<F, R>(modulename: &'static [u8], argc: u32, argv: *mut *mut u8, f: F) -> i32
120where
121	F: Fn(&[&str]) -> R + std::panic::UnwindSafe,
122	R: MainStatusCode,
123{
124	logging::init(modulename);
125	std::panic::catch_unwind(move || {
126		let mut args = Vec::with_capacity(argc as usize);
127		for i in 0..argc {
128			args.push(
129				CStr::from_ptr(*argv.offset(i as isize) as *const c_char)
130					.to_str()
131					.unwrap_or_else(|_| panic!("Invalid UTF-8 in arguments.")),
132			);
133		}
134		f(&args).to_status_code()
135	}).unwrap_or(R::panic_status_code())
136}
137
138/// The return type of your `#[px4_module_main]` function.
139pub trait MainStatusCode {
140	/// The status code to return.
141	fn to_status_code(self) -> i32;
142
143	/// The status code to return in case of a panic.
144	///
145	/// −1 by default.
146	fn panic_status_code() -> i32 {
147		-1
148	}
149}
150
151/// Returns 0.
152impl MainStatusCode for () {
153	fn to_status_code(self) -> i32 {
154		0
155	}
156}
157
158/// Returns the `i32` itself.
159impl MainStatusCode for i32 {
160	fn to_status_code(self) -> i32 {
161		self
162	}
163}
164
165/// Returns 0 for `Ok`, and 1 for `Err`.
166impl MainStatusCode for Result<(), ()> {
167	fn to_status_code(self) -> i32 {
168		match self {
169			Ok(()) => 0,
170			Err(()) => 1,
171		}
172	}
173}
174
175/// Returns 0 for `Ok`, and the `i32` itself for `Err`.
176impl MainStatusCode for Result<(), i32> {
177	fn to_status_code(self) -> i32 {
178		match self {
179			Ok(()) => 0,
180			Err(s) => s,
181		}
182	}
183}