Skip to content

Commit

Permalink
RSDK-5770 - implement GetReadings API for all sensor types
Browse files Browse the repository at this point in the history
  • Loading branch information
gvaradarajan committed Dec 4, 2023
1 parent f16fb95 commit 6f86530
Show file tree
Hide file tree
Showing 21 changed files with 476 additions and 50 deletions.
8 changes: 7 additions & 1 deletion micro-rdk-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@ edition = "2021"
proc-macro = true

[dependencies]
proc-macro-crate = "2.0.0"
proc-macro2 = "1.0.67"
quote = "1.0.33"
syn = "1.0.109"

[dev-dependencies]
anyhow = { version = "1", features = ["backtrace"] }
micro-rdk = { path = "../", features = ["native"] }

[[test]]
name = "do-command-tests"
path = "tests/test.rs"
path = "tests/test.rs"
117 changes: 116 additions & 1 deletion micro-rdk-macros/src/lib.rs
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()
}
173 changes: 159 additions & 14 deletions micro-rdk-macros/tests/test.rs
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)
}
}
3 changes: 1 addition & 2 deletions src/common/adxl345.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use crate::google;

use super::board::Board;
use super::config::ConfigType;
use super::generic::DoCommand;
use super::i2c::I2CHandle;
use super::movement_sensor::MovementSensorType;
use super::registry::{get_board_from_dependencies, ComponentRegistry, Dependency};
Expand All @@ -31,7 +30,7 @@ pub(crate) fn register_models(registry: &mut ComponentRegistry) {
const READING_START_REGISTER: u8 = 50;
const STANDBY_MODE_REGISTER: u8 = 45;

#[derive(DoCommand)]
#[derive(DoCommand, MovementSensorReadings)]
pub struct ADXL345 {
i2c_handle: I2cHandleType,
i2c_address: u8,
Expand Down
1 change: 0 additions & 1 deletion src/common/gpio_motor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ use super::config::ConfigType;
use super::encoder::{
Encoder, EncoderPositionType, EncoderType, COMPONENT_NAME as EncoderCompName,
};
use super::generic::DoCommand;
use super::math_utils::go_for_math;
use super::motor::{
Motor, MotorPinType, MotorPinsConfig, MotorSupportedProperties, MotorType,
Expand Down
1 change: 0 additions & 1 deletion src/common/gpio_servo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ use super::{
actuator::Actuator,
board::{Board, BoardType},
config::{AttributeError, ConfigType},
generic::DoCommand,
registry::{get_board_from_dependencies, ComponentRegistry, Dependency},
servo::{Servo, ServoType},
status::Status,
Expand Down
3 changes: 1 addition & 2 deletions src/common/ina.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ use std::sync::{Arc, Mutex};
use super::{
board::Board,
config::ConfigType,
generic::DoCommand,
i2c::I2cHandleType,
power_sensor::{Current, PowerSensor, PowerSensorType, PowerSupplyType, Voltage},
registry::{get_board_from_dependencies, ComponentRegistry, Dependency},
Expand Down Expand Up @@ -119,7 +118,7 @@ impl fmt::Display for Model {
}
}

#[derive(DoCommand)]
#[derive(DoCommand, PowerSensorReadings)]
struct Ina<H: I2CHandle> {
model: Model,
i2c_handle: H,
Expand Down
Loading

0 comments on commit 6f86530

Please sign in to comment.