Improve the examples

This commit is contained in:
Mihai Dinculescu
2022-10-12 23:27:19 +01:00
parent 9b7e3b97db
commit 307b83ea95
11 changed files with 367 additions and 100 deletions

View File

@@ -1,10 +1,59 @@
# SimConnect SDK in Rust # SimConnect SDK in Rust
## Running the examples ## Usage
```bash ```toml
git clone git@github.com:mihai-dinculescu/simconnect-sdk.git [dependencies]
cd simconnect-sdk simconnect-sdk = { version = "0.1", features = ["derive"] }
RUST_LOG=info cargo run --bin basic
RUST_LOG=info cargo run --bin basic_no_macros
``` ```
```rust
use simconnect_sdk::{Notification, SimConnect, SimConnectObject};
/// A data structure that will be used to receive data from SimConnect.
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second")]
struct GpsData {
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
lat: f64,
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
lon: f64,
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
alt: f64,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = SimConnect::new("Simple Program");
match client {
Ok(mut client) => loop {
let notification = client.get_next_dispatch()?;
match notification {
Some(Notification::Open) => {
println!("Open");
// The struct must be registered after the connection is successfully open
client.register_object::<GpsData>()?;
}
Some(Notification::Data(data)) => {
if let Ok(gps_data) = GpsData::try_from(&data) {
println!("GPS Data: {gps_data:?}");
}
}
_ => (),
}
// sleep for about a frame to reduce CPU usage
std::thread::sleep(std::time::Duration::from_millis(16));
},
Err(e) => {
println!("Error: {e:?}")
}
}
Ok(())
}
```
See [more examples](https://github.com/mihai-dinculescu/simconnect-sdk/tree/main/examples).

View File

@@ -12,8 +12,16 @@ name = "basic"
path = "src/basic.rs" path = "src/basic.rs"
[[bin]] [[bin]]
name = "basic_no_macros" name = "basic_with_tracing"
path = "src/basic_no_macros.rs" path = "src/basic_with_tracing.rs"
[[bin]]
name = "basic_without_macro"
path = "src/basic_without_macro.rs"
[[bin]]
name = "multiple_objects"
path = "src/multiple_objects.rs"
[dependencies] [dependencies]
tracing = "0.1" tracing = "0.1"

32
examples/README.md Normal file
View File

@@ -0,0 +1,32 @@
# SimConnect SDK Examples
## Checkout the repository
```bash
git clone git@github.com:mihai-dinculescu/simconnect-sdk.git
cd simconnect-sdk
```
## Receiving data
```bash
cargo run --bin basic
```
## Using tracing
```bash
RUST_LOG=info cargo run --bin basic_with_tracing
```
## Receiving data without the derive macro
```bash
cargo run --bin basic_without_macro
```
## Multiple objects
```bash
cargo run --bin multiple_objects
```

View File

@@ -1,9 +1,8 @@
#![allow(dead_code)] #![allow(dead_code)]
use simconnect_sdk::{Notification, SimConnect, SimConnectObject}; use simconnect_sdk::{Notification, SimConnect, SimConnectObject};
use simconnect_sdk_examples::setup_logging;
use tracing::{error, info};
/// A data structure that will be used to receive data from SimConnect.
#[derive(Debug, Clone, SimConnectObject)] #[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second")] #[simconnect(period = "second")]
struct GpsData { struct GpsData {
@@ -13,22 +12,9 @@ struct GpsData {
lon: f64, lon: f64,
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")] #[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
alt: f64, alt: f64,
#[simconnect(name = "GPS GROUND MAGNETIC TRACK", unit = "degrees")]
gps_ground_magnetic_track: f64,
#[simconnect(name = "GPS GROUND SPEED", unit = "Meters per second")]
gps_ground_speed: f64,
}
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", condition = "changed")]
pub struct OnGround {
#[simconnect(name = "SIM ON GROUND", unit = "bool")]
sim_on_ground: bool,
} }
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
setup_logging()?;
let client = SimConnect::new("Simple Program"); let client = SimConnect::new("Simple Program");
match client { match client {
@@ -37,26 +23,24 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
match notification { match notification {
Some(Notification::Open) => { Some(Notification::Open) => {
info!("Open"); println!("Open");
// The struct must be registered after the connection is successfully open
client.register_object::<GpsData>()?; client.register_object::<GpsData>()?;
client.register_object::<OnGround>()?;
} }
Some(Notification::Data(data)) => { Some(Notification::Data(data)) => {
if let Ok(gps_data) = GpsData::try_from(&data) { if let Ok(gps_data) = GpsData::try_from(&data) {
info!("GPS Data: {gps_data:?}"); println!("GPS Data: {gps_data:?}");
continue;
}
if let Ok(on_ground) = OnGround::try_from(&data) {
info!("On Ground data: {on_ground:?}");
continue;
} }
} }
_ => (), _ => (),
} }
// sleep for about a frame to reduce CPU usage
std::thread::sleep(std::time::Duration::from_millis(16));
}, },
Err(e) => { Err(e) => {
error!("{e:?}") println!("Error: {e:?}")
} }
} }

View File

@@ -0,0 +1,66 @@
#![allow(dead_code)]
use simconnect_sdk::{Notification, SimConnect, SimConnectObject};
use tracing::{error, info};
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
/// A data structure that will be used to receive data from SimConnect.
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second")]
struct GpsData {
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
lat: f64,
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
lon: f64,
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
alt: f64,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
setup_logging()?;
let client = SimConnect::new("Simple Program");
match client {
Ok(mut client) => loop {
let notification = client.get_next_dispatch()?;
match notification {
Some(Notification::Open) => {
info!("Open");
// The struct must be registered after the connection is successfully open
client.register_object::<GpsData>()?;
}
Some(Notification::Data(data)) => {
if let Ok(gps_data) = GpsData::try_from(&data) {
info!("GPS Data: {gps_data:?}");
}
}
_ => (),
}
// sleep for about a frame to reduce CPU usage
std::thread::sleep(std::time::Duration::from_millis(16));
},
Err(e) => {
error!("{e:?}")
}
}
Ok(())
}
fn setup_logging() -> Result<(), Box<dyn std::error::Error>> {
let filter_layer = EnvFilter::try_from_default_env().or_else(|_| EnvFilter::try_new("info"))?;
let fmt_layer = fmt::layer()
.with_target(false)
.with_span_events(fmt::format::FmtSpan::FULL);
tracing_subscriber::registry()
.with(filter_layer)
.with(fmt_layer)
.init();
Ok(())
}

View File

@@ -4,17 +4,13 @@ use simconnect_sdk::{
Condition, DataType, Notification, NotificationData, Period, SimConnect, SimConnectError, Condition, DataType, Notification, NotificationData, Period, SimConnect, SimConnectError,
SimConnectObjectExt, SimConnectObjectExt,
}; };
use tracing::{error, info};
use simconnect_sdk_examples::setup_logging;
/// A data structure that will be used to receive data from SimConnect.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct GpsData { pub struct GpsData {
lat: f64, lat: f64,
lon: f64, lon: f64,
alt: f64, alt: f64,
gps_ground_magnetic_track: f64,
gps_ground_speed: f64,
} }
impl SimConnectObjectExt for GpsData { impl SimConnectObjectExt for GpsData {
@@ -22,18 +18,7 @@ impl SimConnectObjectExt for GpsData {
client.add_to_data_definition(id, "PLANE LATITUDE", "degrees", DataType::Float64)?; client.add_to_data_definition(id, "PLANE LATITUDE", "degrees", DataType::Float64)?;
client.add_to_data_definition(id, "PLANE LONGITUDE", "degrees", DataType::Float64)?; client.add_to_data_definition(id, "PLANE LONGITUDE", "degrees", DataType::Float64)?;
client.add_to_data_definition(id, "PLANE ALTITUDE", "meters", DataType::Float64)?; client.add_to_data_definition(id, "PLANE ALTITUDE", "meters", DataType::Float64)?;
client.add_to_data_definition(
id,
"GPS GROUND MAGNETIC TRACK",
"degrees",
DataType::Float64,
)?;
client.add_to_data_definition(
id,
"GPS GROUND SPEED",
"Meters per second",
DataType::Float64,
)?;
client.request_data_on_sim_object(id, Period::Second, Condition::None, 0)?; client.request_data_on_sim_object(id, Period::Second, Condition::None, 0)?;
Ok(()) Ok(())
@@ -48,31 +33,7 @@ impl TryFrom<&NotificationData> for GpsData {
} }
} }
#[derive(Debug, Clone)]
pub struct OnGround {
sim_on_ground: bool,
}
impl SimConnectObjectExt for OnGround {
fn register(client: &mut SimConnect, id: u32) -> Result<(), SimConnectError> {
client.add_to_data_definition(id, "SIM ON GROUND", "bool", DataType::Bool)?;
client.request_data_on_sim_object(id, Period::Second, Condition::Changed, 0)?;
Ok(())
}
}
impl TryFrom<&NotificationData> for OnGround {
type Error = SimConnectError;
fn try_from(value: &NotificationData) -> Result<Self, Self::Error> {
value.try_transmute::<OnGround>()
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
setup_logging()?;
let client = SimConnect::new("Simple Program"); let client = SimConnect::new("Simple Program");
match client { match client {
@@ -81,26 +42,24 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
match notification { match notification {
Some(Notification::Open) => { Some(Notification::Open) => {
info!("Open"); println!("Open");
// The struct must be registered after the connection is successfully open
client.register_object::<GpsData>()?; client.register_object::<GpsData>()?;
client.register_object::<OnGround>()?;
} }
Some(Notification::Data(data)) => { Some(Notification::Data(data)) => {
if let Ok(gps_data) = GpsData::try_from(&data) { if let Ok(gps_data) = GpsData::try_from(&data) {
info!("GPS Data: {gps_data:?}"); println!("GPS Data: {gps_data:?}");
continue;
}
if let Ok(on_ground) = OnGround::try_from(&data) {
info!("On Ground data: {on_ground:?}");
continue;
} }
} }
_ => (), _ => (),
} }
// sleep for about a frame to reduce CPU usage
std::thread::sleep(std::time::Duration::from_millis(16));
}, },
Err(e) => { Err(e) => {
error!("{e:?}") println!("Error: {e:?}")
} }
} }

View File

@@ -1,15 +0,0 @@
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
pub fn setup_logging() -> Result<(), Box<dyn std::error::Error>> {
let filter_layer = EnvFilter::try_from_default_env().or_else(|_| EnvFilter::try_new("info"))?;
let fmt_layer = fmt::layer()
.with_target(false)
.with_span_events(fmt::format::FmtSpan::FULL);
tracing_subscriber::registry()
.with(filter_layer)
.with(fmt_layer)
.init();
Ok(())
}

View File

@@ -0,0 +1,64 @@
#![allow(dead_code)]
use simconnect_sdk::{Notification, SimConnect, SimConnectObject};
/// A data structure that will be used to receive data from SimConnect.
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second")]
struct GpsData {
#[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
lat: f64,
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
lon: f64,
#[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
alt: f64,
}
/// A second data structure that will be used to receive data from SimConnect.
#[derive(Debug, Clone, SimConnectObject)]
#[simconnect(period = "second", condition = "changed")]
pub struct OnGround {
#[simconnect(name = "SIM ON GROUND", unit = "bool")]
sim_on_ground: bool,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = SimConnect::new("Simple Program");
match client {
Ok(mut client) => loop {
let notification = client.get_next_dispatch()?;
match notification {
Some(Notification::Open) => {
println!("Open");
// The structs must be registered after the connection is successfully open
client.register_object::<GpsData>()?;
client.register_object::<OnGround>()?;
}
Some(Notification::Data(data)) => {
if let Ok(gps_data) = GpsData::try_from(&data) {
println!("GPS Data: {gps_data:?}");
// We've already got our object, there's no point in trying another in this iteration
continue;
}
if let Ok(on_ground) = OnGround::try_from(&data) {
println!("On Ground data: {on_ground:?}");
// We've already got our object, there's no point in trying another in this iteration
continue;
}
}
_ => (),
}
// sleep for about a frame to reduce CPU usage
std::thread::sleep(std::time::Duration::from_millis(16));
},
Err(e) => {
println!("Error: {e:?}")
}
}
Ok(())
}

View File

@@ -18,3 +18,10 @@ num_enum = "0.5"
tracing = "0.1" tracing = "0.1"
thiserror = "1.0" thiserror = "1.0"
simconnect-sdk-derive = { path = "../simconnect-sdk-derive", optional = true } simconnect-sdk-derive = { path = "../simconnect-sdk-derive", optional = true }
# docs.rs-specific configuration
[package.metadata.docs.rs]
# document all features
all-features = true
# defines the configuration attribute `docsrs`
rustdoc-args = ["--cfg", "docsrs"]

View File

@@ -1,7 +1,63 @@
//! # SimConnect SDK //! # SimConnect SDK
//! SimConnect SDK in Rust. //! SimConnect SDK in Rust.
//! //!
//! See [examples](https://github.com/mihai-dinculescu/simconnect-sdk/tree/main/examples/src). //! ## Usage
//! ```toml
//! [dependencies]
//! simconnect-sdk = { version = "0.1", features = ["derive"] }
//! ```
//!
//! ```no_run
//! use simconnect_sdk::{Notification, SimConnect, SimConnectObject};
//!
//! /// A data structure that will be used to receive data from SimConnect.
//! #[derive(Debug, Clone, SimConnectObject)]
//! #[simconnect(period = "second")]
//! struct GpsData {
//! #[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
//! lat: f64,
//! #[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
//! lon: f64,
//! #[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
//! alt: f64,
//! }
//!
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let client = SimConnect::new("Simple Program");
//!
//! match client {
//! Ok(mut client) => loop {
//! let notification = client.get_next_dispatch()?;
//!
//! match notification {
//! Some(Notification::Open) => {
//! println!("Open");
//!
//! // The struct must be registered after the connection is successfully open
//! client.register_object::<GpsData>()?;
//! }
//! Some(Notification::Data(data)) => {
//! if let Ok(gps_data) = GpsData::try_from(&data) {
//! println!("GPS Data: {gps_data:?}");
//! }
//! }
//! _ => (),
//! }
//!
//! // sleep for about a frame to reduce CPU usage
//! std::thread::sleep(std::time::Duration::from_millis(16));
//! },
//! Err(e) => {
//! println!("Error: {e:?}")
//! }
//! }
//!
//! Ok(())
//! }
//! ```
//!
//! See [more examples](https://github.com/mihai-dinculescu/simconnect-sdk/tree/main/examples).
mod bindings; mod bindings;
mod domain; mod domain;
@@ -19,6 +75,8 @@ pub use simconnect::SimConnect;
pub use simconnect_object_ext::SimConnectObjectExt; pub use simconnect_object_ext::SimConnectObjectExt;
#[cfg(feature = "simconnect-sdk-derive")] #[cfg(feature = "simconnect-sdk-derive")]
#[cfg_attr(docsrs, doc(cfg(feature = "simconnect-sdk-derive")))]
extern crate simconnect_sdk_derive; extern crate simconnect_sdk_derive;
#[cfg(feature = "simconnect-sdk-derive")] #[cfg(feature = "simconnect-sdk-derive")]
#[cfg_attr(docsrs, doc(cfg(feature = "simconnect-sdk-derive")))]
pub use simconnect_sdk_derive::*; pub use simconnect_sdk_derive::*;

View File

@@ -7,13 +7,65 @@ use crate::{
}; };
/// SimConnect SDK Client. /// SimConnect SDK Client.
///
/// # Example
///
/// ```no_run
/// use simconnect_sdk::{Notification, SimConnect, SimConnectObject};
///
/// /// A data structure that will be used to receive data from SimConnect.
/// #[derive(Debug, Clone, SimConnectObject)]
/// #[simconnect(period = "second")]
/// struct GpsData {
/// #[simconnect(name = "PLANE LATITUDE", unit = "degrees")]
/// lat: f64,
/// #[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
/// lon: f64,
/// #[simconnect(name = "PLANE LONGITUDE", unit = "degrees")]
/// alt: f64,
/// }
///
/// fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let client = SimConnect::new("Simple Program");
///
/// match client {
/// Ok(mut client) => loop {
/// let notification = client.get_next_dispatch()?;
///
/// match notification {
/// Some(Notification::Open) => {
/// println!("Open");
///
/// // The struct must be registered after the connection is successfully open
/// client.register_object::<GpsData>()?;
/// }
/// Some(Notification::Data(data)) => {
/// if let Ok(gps_data) = GpsData::try_from(&data) {
/// println!("GPS Data: {gps_data:?}");
/// }
/// }
/// _ => (),
/// }
///
/// // sleep for about a frame to reduce CPU usage
/// std::thread::sleep(std::time::Duration::from_millis(16));
/// },
/// Err(e) => {
/// println!("Error: {e:?}")
/// }
/// }
///
/// Ok(())
/// }
/// ```
#[derive(Debug)] #[derive(Debug)]
pub struct SimConnect { pub struct SimConnect {
pub handle: std::ptr::NonNull<c_void>, handle: std::ptr::NonNull<c_void>,
pub registered_objects: Vec<String>, registered_objects: Vec<String>,
} }
impl SimConnect { impl SimConnect {
/// Create a new SimConnect SDK client.
#[tracing::instrument(name = "SimConnect::new")] #[tracing::instrument(name = "SimConnect::new")]
pub fn new(name: &str) -> Result<Self, SimConnectError> { pub fn new(name: &str) -> Result<Self, SimConnectError> {
let mut handle = std::ptr::null_mut(); let mut handle = std::ptr::null_mut();
@@ -182,6 +234,9 @@ impl SimConnect {
Ok(()) Ok(())
} }
/// Receive the next SimConnect message.
/// This is a non-blocking function. If there are no messages to receive, it will return None immediately.
/// When called in a loop, it is recommended to use a short sleep time.
pub fn get_next_dispatch(&self) -> Result<Option<Notification>, SimConnectError> { pub fn get_next_dispatch(&self) -> Result<Option<Notification>, SimConnectError> {
let mut data_buf: *mut bindings::SIMCONNECT_RECV = std::ptr::null_mut(); let mut data_buf: *mut bindings::SIMCONNECT_RECV = std::ptr::null_mut();
let mut size_buf: bindings::DWORD = 32; let mut size_buf: bindings::DWORD = 32;