xplane-datamonitor

A WIP tool to show data inside grafana
git clone git://archive.git.mtrnord.blog/MTRNord/xplane-datamonitor.git
Log | Files | Refs

commit 89a7457aab9f8d5c44d110f988cbf2e76b048a59
Author: MTRNord <mtrnord1@gmail.com>
Date:   Thu,  5 Aug 2021 02:16:51 +0200

initial commit

Diffstat:
A.gitignore | 2++
ACargo.toml | 21+++++++++++++++++++++
Asrc/energy.rs | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/error.rs | 9+++++++++
Asrc/lib.rs | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/location.rs | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 259 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml @@ -0,0 +1,20 @@ +cargo-features = ["edition2021"] + +[package] +name = "xplane-datamonitor" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +xplm = "0.3.1" +anyhow = "1.0.42" +thiserror = "1.0.26" +tokio = { version = "1", features = ["full"] } + +[profile.release] +lto = true +\ No newline at end of file diff --git a/src/energy.rs b/src/energy.rs @@ -0,0 +1,58 @@ +use std::{ + fmt::Display, + sync::{Arc, RwLock}, +}; + +use xplm::data::{borrowed::DataRef, ArrayRead, DataRead, ReadWrite}; + +use crate::error::Error; + +pub(crate) struct EnergyRefs { + pub(crate) gpu_on: DataRef<bool, ReadWrite>, + pub(crate) battery: DataRef<[i32], ReadWrite>, +} + +unsafe impl Send for EnergyRefs {} +unsafe impl Sync for EnergyRefs {} + +#[derive(Clone)] +pub(crate) struct Energy { + inner: Arc<RwLock<EnergyRefs>>, +} + +impl Energy { + pub(crate) fn new() -> Result<Self, Error> { + Ok(Self { + inner: Arc::new(RwLock::new(EnergyRefs { + gpu_on: DataRef::find("sim/cockpit/electrical/gpu_on")?.writeable()?, + battery: DataRef::find("sim/cockpit2/electrical/battery_on")?.writeable()?, + })), + }) + } + + pub(crate) fn gpu_on(&self) -> Result<bool, Error> { + let lock = self.inner.read().or(Err(Error::UnableToGetLock))?; + Ok(lock.gpu_on.get()) + } + + pub(crate) fn battery_on(&self) -> Result<bool, Error> { + let lock = self.inner.read().or(Err(Error::UnableToGetLock))?; + Ok(lock.battery.as_vec().contains(&1)) + } +} + +impl Display for Energy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let lock = self.inner.read(); + if let Ok(lock) = lock { + write!( + f, + "GPU enabled: {}, Battery enabled: {}", + lock.gpu_on.get(), + lock.battery.as_vec().contains(&1), + ) + } else { + Ok(()) + } + } +} diff --git a/src/error.rs b/src/error.rs @@ -0,0 +1,9 @@ +use thiserror::Error as ThisError; + +#[derive(ThisError, Debug)] +pub(crate) enum Error { + #[error(transparent)] + FindError(#[from] xplm::data::borrowed::FindError), + #[error("Unable to get lock")] + UnableToGetLock, +} diff --git a/src/lib.rs b/src/lib.rs @@ -0,0 +1,112 @@ +use std::{ + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, RwLock, + }, + thread::JoinHandle, +}; +#[deny(unused_imports)] +#[deny(missing_docs)] +use std::{thread, time}; +use tokio::runtime::Runtime; +use xplm::{ + debug, + plugin::{Plugin, PluginInfo}, + xplane_plugin, +}; + +use crate::location::Location; +use crate::{energy::Energy, error::Error}; + +mod energy; +mod error; +mod location; + +#[derive(Clone)] +struct DataMonitorPlugin { + location: Location, + energy: Energy, + thread: Arc<RwLock<Option<JoinHandle<()>>>>, + stopped: Arc<AtomicBool>, +} + +impl DataMonitorPlugin { + pub(crate) fn start(&self) { + debug("[DATAMONITOR] starting\n"); + let self_clone = self.clone(); + let thread = thread::spawn(move || { + let delay = time::Duration::from_secs(5); + { + debug(format!("[DATAMONITOR] {}\n", self_clone.location)); + }; + let rt = Runtime::new().unwrap(); + loop { + // Do stuff + if self_clone.stopped.load(Ordering::Relaxed) { + break; + } + + let current_location = self_clone.location.clone(); + let current_energy = self_clone.energy.clone(); + rt.spawn(async move { + // Do stuff with location + if current_energy.battery_on().unwrap() || current_energy.gpu_on().unwrap() { + debug(format!("[DATAMONITOR] {}\n", current_location)); + debug(format!("[DATAMONITOR] {}\n", current_energy)); + } + }); + thread::sleep(delay); + } + }); + let mut lock = self.thread.write().unwrap(); + *lock = Some(thread); + } +} + +impl Plugin for DataMonitorPlugin { + type Error = Error; + + fn start() -> Result<Self, Self::Error> { + let location = Location::new(); + if let Err(ref e) = location { + debug(format!("[DATAMONITOR][ERROR] Location init: {:?}\n", e)); + } + let energy = Energy::new(); + if let Err(ref e) = energy { + debug(format!("[DATAMONITOR][ERROR] Energy init: {:?}\n", e)); + } + let plugin = DataMonitorPlugin { + location: location.unwrap(), + energy: energy.unwrap(), + thread: Arc::new(RwLock::new(None)), + stopped: Arc::new(AtomicBool::new(false)), + }; + Ok(plugin) + } + fn enable(&mut self) -> Result<(), Self::Error> { + self.start(); + Ok(()) + } + fn disable(&mut self) { + debug("[DATAMONITOR][INFO] Stopping threads\n"); + self.stopped.swap(true, Ordering::Release); + let mut lock = self.thread.write().unwrap(); + let thread = std::mem::replace(&mut *lock, None); + if let Some(thread) = thread { + thread.join().unwrap(); + debug("[DATAMONITOR][INFO] Stopped threads\n"); + } + } + + fn info(&self) -> xplm::plugin::PluginInfo { + PluginInfo { + name: String::from("Datamonitor"), + signature: String::from("dev.nordgedanken.datamonitor"), + description: String::from( + "Gets certain datarefs to display in a grafana for review of flights.", + ), + } + } +} + +xplane_plugin!(DataMonitorPlugin); diff --git a/src/location.rs b/src/location.rs @@ -0,0 +1,57 @@ +use std::{ + fmt::Display, + sync::{Arc, RwLock}, +}; + +use xplm::data::{borrowed::DataRef, DataRead, ReadOnly}; + +use crate::error::Error; + +pub(crate) struct LocationRefs { + pub(crate) latitude: DataRef<f64, ReadOnly>, + pub(crate) longitude: DataRef<f64, ReadOnly>, +} + +unsafe impl Send for LocationRefs {} +unsafe impl Sync for LocationRefs {} + +#[derive(Clone)] +pub(crate) struct Location { + inner: Arc<RwLock<LocationRefs>>, +} + +impl Location { + pub(crate) fn new() -> Result<Self, Error> { + Ok(Self { + inner: Arc::new(RwLock::new(LocationRefs { + latitude: DataRef::find("sim/flightmodel/position/latitude")?, + longitude: DataRef::find("sim/flightmodel/position/longitude")?, + })), + }) + } + + pub(crate) fn lat(&self) -> Result<f64, Error> { + let lock = self.inner.read().or(Err(Error::UnableToGetLock))?; + Ok(lock.latitude.get()) + } + pub(crate) fn lon(&self) -> Result<f64, Error> { + let lock = self.inner.read().or(Err(Error::UnableToGetLock))?; + Ok(lock.longitude.get()) + } +} + +impl Display for Location { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let lock = self.inner.read(); + if let Ok(lock) = lock { + write!( + f, + "Latitude: {}, Longitude: {}", + lock.latitude.get(), + lock.longitude.get() + ) + } else { + Ok(()) + } + } +}