-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
RSDK-5770 - implement GetReadings API for all sensor types
- Loading branch information
1 parent
f16fb95
commit 6f86530
Showing
21 changed files
with
476 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,128 @@ | ||
//! Collection of macros useful for implementing traits for various components. NOTE: THESE | ||
//! MACROS WILL NOT WORK PROPERLY IF YOU RENAME THE MICRO-RDK DEPENDENCY USING THE PACKAGE | ||
//! ATTRIBUTE IN CARGO.TOML. | ||
//! | ||
//! DoCommand - trivially implements the DoCommand trait (most component traits require this trait to be | ||
//! satisfied to allow for driver-specific commands that fall outside of the component's API, but most | ||
//! implementations have no real need for it) | ||
//! | ||
//! MovementSensorReadings - provides a default implementation of the Readings trait for implementers | ||
//! of the MovementSensor trait. `get_generic_readings` will return a struct of key-value pairs for every | ||
//! method that is declared supported by `get_properties` | ||
//! | ||
//! PowerSensorReadings - provides a default implementation of the Readings trait for implementers | ||
//! of the PowerSensor trait. `get_generic_readings` will return a struct containing the voltage (in volts), | ||
//! current (in amperes), power (in watts), and whether or not the power supply is AC. | ||
//! | ||
//! # Example using `MovementSensorReadings` | ||
//! | ||
//! ```ignore | ||
//! use std::collections::HashMap; | ||
//! use micro_rdk::common::{ | ||
//! movement_sensor::{MovementSensor, MovementSensorSupportedMethods}, | ||
//! status::Status, | ||
//! }; | ||
//! use micro_rdk::{DoCommand, MovementSensorReadings}; | ||
//! | ||
//! #[derive(DoCommand, MovementSensorReadings)] | ||
//! pub struct MyMovementSensor {} | ||
//! | ||
//! impl MovementSensor for MyMovementSensor { | ||
//! fn get_angular_velocity(&mut self) -> anyhow::Result<micro_rdk::common::math_utils::Vector3> { | ||
//! anyhow::bail!("unimplemented") | ||
//! } | ||
//! fn get_compass_heading(&mut self) -> anyhow::Result<f64> { | ||
//! Ok(25.0) | ||
//! } | ||
//! fn get_linear_acceleration( | ||
//! &mut self, | ||
//! ) -> anyhow::Result<micro_rdk::common::math_utils::Vector3> { | ||
//! anyhow::bail!("unimplemented") | ||
//! } | ||
//! fn get_linear_velocity(&mut self) -> anyhow::Result<micro_rdk::common::math_utils::Vector3> { | ||
//! anyhow::bail!("unimplemented") | ||
//! } | ||
//! fn get_position(&mut self) -> anyhow::Result<micro_rdk::common::movement_sensor::GeoPosition> { | ||
//! anyhow::bail!("unimplemented") | ||
//! } | ||
//! fn get_properties(&self) -> MovementSensorSupportedMethods { | ||
//! MovementSensorSupportedMethods { | ||
//! position_supported: false, | ||
//! linear_velocity_supported: false, | ||
//! angular_velocity_supported: false, | ||
//! linear_acceleration_supported: false, | ||
//! compass_heading_supported: false, | ||
//! } | ||
//! } | ||
//! } | ||
//! | ||
//! impl Status for MyMovementSensor { | ||
//! fn get_status(&self) -> anyhow::Result<Option<micro_rdk::google::protobuf::Struct>> { | ||
//! Ok(Some(micro_rdk::google::protobuf::Struct { | ||
//! fields: HashMap::new(), | ||
//! })) | ||
//! } | ||
//! } | ||
//! ``` | ||
use proc_macro::TokenStream; | ||
use proc_macro2::Span; | ||
use proc_macro_crate::{crate_name, FoundCrate}; | ||
use quote::quote; | ||
use syn::Ident; | ||
|
||
fn get_micro_rdk_crate_ident() -> Ident { | ||
let found_crate = crate_name("micro-rdk").expect("micro-rdk is present in `Cargo.toml`"); | ||
match found_crate { | ||
FoundCrate::Itself => Ident::new("crate", Span::call_site()), | ||
FoundCrate::Name(name) => Ident::new(&name, Span::call_site()), | ||
} | ||
} | ||
|
||
#[proc_macro_derive(DoCommand)] | ||
pub fn impl_do_command_default(input: TokenStream) -> TokenStream { | ||
let ast: syn::DeriveInput = syn::parse(input).unwrap(); | ||
let name = &ast.ident; | ||
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); | ||
|
||
let crate_ident = get_micro_rdk_crate_ident(); | ||
let gen = quote! { | ||
impl #impl_generics DoCommand for #name #ty_generics #where_clause {} | ||
impl #impl_generics #crate_ident::common::generic::DoCommand for #name #ty_generics #where_clause {} | ||
}; | ||
gen.into() | ||
} | ||
|
||
#[proc_macro_derive(MovementSensorReadings)] | ||
pub fn impl_readings_for_movement_sensor(input: TokenStream) -> TokenStream { | ||
let ast: syn::DeriveInput = syn::parse(input).unwrap(); | ||
let name = &ast.ident; | ||
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); | ||
|
||
let crate_ident = get_micro_rdk_crate_ident(); | ||
let gen = quote! { | ||
impl #impl_generics #crate_ident::common::sensor::Readings for #name #ty_generics #where_clause { | ||
fn get_generic_readings(&mut self) -> anyhow::Result<#crate_ident::common::sensor::GenericReadingsResult> { | ||
#crate_ident::common::movement_sensor::get_movement_sensor_generic_readings(self) | ||
} | ||
} | ||
}; | ||
gen.into() | ||
} | ||
|
||
#[proc_macro_derive(PowerSensorReadings)] | ||
pub fn impl_readings_for_power_sensor(input: TokenStream) -> TokenStream { | ||
let ast: syn::DeriveInput = syn::parse(input).unwrap(); | ||
let name = &ast.ident; | ||
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); | ||
|
||
let crate_ident = get_micro_rdk_crate_ident(); | ||
let gen = quote! { | ||
impl #impl_generics #crate_ident::common::sensor::Readings for #name #ty_generics #where_clause { | ||
fn get_generic_readings(&mut self) -> anyhow::Result<#crate_ident::common::sensor::GenericReadingsResult> { | ||
#crate_ident::common::power_sensor::get_power_sensor_generic_readings(self) | ||
} | ||
} | ||
}; | ||
|
||
gen.into() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,172 @@ | ||
use micro_rdk_macros::DoCommand; | ||
use micro_rdk::common::math_utils::Vector3; | ||
use micro_rdk::common::movement_sensor::{ | ||
GeoPosition, MovementSensor, MovementSensorSupportedMethods, | ||
}; | ||
use micro_rdk::common::power_sensor::{Current, PowerSensor, PowerSupplyType, Voltage}; | ||
use micro_rdk::common::sensor::Readings; | ||
use micro_rdk::common::status::Status; | ||
use micro_rdk::google::protobuf::value::Kind; | ||
use micro_rdk_macros::{DoCommand, MovementSensorReadings, PowerSensorReadings}; | ||
use std::collections::HashMap; | ||
|
||
pub trait DoCommand { | ||
fn do_command(&self) -> u8 { | ||
1 | ||
#[derive(DoCommand)] | ||
struct TestDoCommandStruct {} | ||
|
||
#[derive(DoCommand, MovementSensorReadings)] | ||
struct TestMovementSensor {} | ||
|
||
impl MovementSensor for TestMovementSensor { | ||
fn get_position(&mut self) -> anyhow::Result<GeoPosition> { | ||
Ok(GeoPosition { | ||
lat: 1.0, | ||
lon: 2.0, | ||
alt: 3.0, | ||
}) | ||
} | ||
|
||
fn get_linear_acceleration(&mut self) -> anyhow::Result<Vector3> { | ||
Ok(Vector3 { | ||
x: 0.0, | ||
y: 1.0, | ||
z: 2.0, | ||
}) | ||
} | ||
|
||
fn get_properties(&self) -> MovementSensorSupportedMethods { | ||
MovementSensorSupportedMethods { | ||
position_supported: true, | ||
linear_acceleration_supported: true, | ||
linear_velocity_supported: false, | ||
angular_velocity_supported: false, | ||
compass_heading_supported: true, | ||
} | ||
} | ||
|
||
fn get_linear_velocity(&mut self) -> anyhow::Result<Vector3> { | ||
anyhow::bail!("unimplemented: movement_sensor_get_linear_velocity") | ||
} | ||
|
||
fn get_angular_velocity(&mut self) -> anyhow::Result<Vector3> { | ||
anyhow::bail!("unimplemented: movement_sensor_get_angular_velocity") | ||
} | ||
|
||
fn get_compass_heading(&mut self) -> anyhow::Result<f64> { | ||
Ok(3.5) | ||
} | ||
} | ||
|
||
pub trait TestTrait: DoCommand { | ||
fn test_fn(&self) -> u8; | ||
impl Status for TestMovementSensor { | ||
fn get_status(&self) -> anyhow::Result<Option<micro_rdk::google::protobuf::Struct>> { | ||
Ok(Some(micro_rdk::google::protobuf::Struct { | ||
fields: HashMap::new(), | ||
})) | ||
} | ||
} | ||
|
||
#[derive(DoCommand)] | ||
pub struct TestStruct {} | ||
#[derive(DoCommand, PowerSensorReadings)] | ||
struct TestPowerSensor {} | ||
|
||
impl PowerSensor for TestPowerSensor { | ||
fn get_voltage(&mut self) -> anyhow::Result<Voltage> { | ||
Ok(Voltage { | ||
volts: 5.0, | ||
power_supply_type: PowerSupplyType::AC, | ||
}) | ||
} | ||
|
||
fn get_current(&mut self) -> anyhow::Result<Current> { | ||
Ok(Current { | ||
amperes: 6.0, | ||
power_supply_type: PowerSupplyType::AC, | ||
}) | ||
} | ||
|
||
impl TestTrait for TestStruct { | ||
fn test_fn(&self) -> u8 { | ||
2 | ||
fn get_power(&mut self) -> anyhow::Result<f64> { | ||
Ok(7.0) | ||
} | ||
} | ||
|
||
impl Status for TestPowerSensor { | ||
fn get_status(&self) -> anyhow::Result<Option<micro_rdk::google::protobuf::Struct>> { | ||
Ok(Some(micro_rdk::google::protobuf::Struct { | ||
fields: HashMap::new(), | ||
})) | ||
} | ||
} | ||
|
||
#[test] | ||
fn do_command_derive() { | ||
let a = TestStruct {}; | ||
assert_eq!(a.do_command(), 1); | ||
assert_eq!(a.test_fn(), 2); | ||
use micro_rdk::common::generic::DoCommand; | ||
let mut a = TestDoCommandStruct {}; | ||
assert!(a.do_command(None).is_err()); | ||
} | ||
|
||
#[test] | ||
fn movement_sensor_readings_derive() { | ||
let mut a = TestMovementSensor {}; | ||
let res = a.get_generic_readings(); | ||
assert!(res.is_ok()); | ||
let res = res.unwrap(); | ||
|
||
// test position | ||
let pos = res.get("position"); | ||
assert!(pos.is_some()); | ||
let pos = &pos.unwrap().kind; | ||
assert!(pos.is_some()); | ||
let pos = pos.as_ref().unwrap(); | ||
if let Kind::StructValue(pos_struct) = pos { | ||
let lat_val = pos_struct.fields.get("lat"); | ||
assert!(lat_val.is_some()); | ||
if let Some(Kind::NumberValue(lat)) = lat_val.unwrap().kind { | ||
assert_eq!(lat, 1.0); | ||
} else { | ||
assert!(false) | ||
} | ||
} else { | ||
assert!(false) | ||
} | ||
|
||
// test acceleration | ||
let acc = res.get("linear_acceleration"); | ||
assert!(acc.is_some()); | ||
let acc = &acc.unwrap().kind; | ||
assert!(acc.is_some()); | ||
let acc = acc.as_ref().unwrap(); | ||
if let Kind::StructValue(acc_struct) = acc { | ||
let y_val = acc_struct.fields.get("y"); | ||
assert!(y_val.is_some()); | ||
if let Some(Kind::NumberValue(y)) = y_val.unwrap().kind { | ||
assert_eq!(y, 1.0); | ||
} else { | ||
assert!(false) | ||
} | ||
} else { | ||
assert!(false) | ||
} | ||
} | ||
|
||
#[test] | ||
fn power_sensor_readings_derive() { | ||
let mut a = TestPowerSensor {}; | ||
let res = a.get_generic_readings(); | ||
assert!(res.is_ok()); | ||
let res = res.unwrap(); | ||
|
||
let volts = res.get("volts"); | ||
assert!(volts.is_some()); | ||
let volts = &volts.unwrap().kind; | ||
assert!(volts.is_some()); | ||
let volts = volts.as_ref().unwrap(); | ||
if let Kind::NumberValue(volts) = volts { | ||
assert_eq!(*volts, 5.0) | ||
} | ||
|
||
let is_ac = res.get("is_ac"); | ||
assert!(is_ac.is_some()); | ||
let is_ac = &is_ac.unwrap().kind; | ||
assert!(is_ac.is_some()); | ||
let is_ac = is_ac.as_ref().unwrap(); | ||
if let Kind::BoolValue(is_ac) = is_ac { | ||
assert!(is_ac) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.