Removed a bunch of over abstracted bullshit.

This commit is contained in:
Zachary Sunforge
2024-06-22 09:45:12 -07:00
parent 02ddde0c5d
commit a95cb64941
35 changed files with 26 additions and 1600 deletions

View File

@ -6,18 +6,10 @@ members = [
# Device types
"node",
"commander",
# Peripherals
# Peripheral components
"peripheral-components/ads1256/*",
# Macros
"macros/node-poll-variants",
# Examples
"examples/ads1256"
]
[workspace.package]
version = "0.2.1"
version = "0.3.0"
edition = "2021"
repository = "https://git.bfpower.io/BFPOWER/physical"
readme = "README.md"
@ -115,6 +107,7 @@ license.workspace = true
thermocouple_k = []
lm35 = []
pid = []
stm32 = []
[dependencies]
uom = { workspace = true }

View File

@ -1,14 +0,0 @@
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}

View File

@ -1,9 +0,0 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
# replace STM32F429ZITx with your chip as listed in `probe-rs chip list`
runner = "probe-rs run --chip STM32F411CEUx"
[build]
target = "thumbv7em-none-eabi"
[env]
DEFMT_LOG = "trace"

View File

@ -1,47 +0,0 @@
[package]
name = "ads1256-examples"
description = "Examples using the ads1256."
version.workspace = true
edition.workspace = true
repository.workspace = true
readme.workspace = true
license.workspace = true
[dependencies.physical]
path = "../.."
features = ["thermocouple_k", "lm35"]
[dependencies.physical-node]
path = "../../node"
features = ["embassy-sync"]
[dependencies.physical-ads1256]
path = "../../peripheral-components/ads1256/node"
features = ["poll"]
[dependencies.static_cell]
workspace = true
[dependencies.ads1256]
workspace = true
[dependencies.uom]
workspace = true
[dependencies.defmt]
workspace = true
[dependencies.defmt-rtt]
workspace = true
[dependencies.cortex-m]
workspace = true
features = ["critical-section-single-core"]
[dependencies.cortex-m-rt]
workspace = true
[dependencies.embassy-stm32]
workspace = true
features = ["stm32f411ce", "memory-x", "time", "exti", "time-driver-any"]
[dependencies.embassy-executor]
workspace = true
[dependencies.embassy-futures]
workspace = true
[dependencies.embassy-time]
workspace = true
features = ["tick-hz-16_000_000"]
[dependencies.embassy-sync]
workspace = true
[dependencies.panic-probe]
workspace = true

View File

@ -1,5 +0,0 @@
fn main() {
println!("cargo:rustc-link-arg-bins=--nmagic");
println!("cargo:rustc-link-arg-bins=-Tlink.x");
println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
}

View File

@ -1,177 +0,0 @@
#![no_std]
#![no_main]
use core::cell::Cell;
use cortex_m::prelude::_embedded_hal_blocking_delay_DelayUs;
use {defmt_rtt as _, panic_probe as _};
use {embassy_executor as executor, embassy_stm32 as stm32};
use ads1256::standard::input::SingleEnded;
use ads1256::{
AdControl, Ads1256, AutoCal, BitOrder, Buffer, ClockOut, Config, DataRate, DigitalIo,
DigitalIoDirection, DigitalIoState, Gain, Multiplexer, MuxInput, Sdcs, Status,
};
use embassy_time::Delay;
use executor::Spawner;
use stm32::dma::NoDma;
use stm32::exti::ExtiInput;
use stm32::gpio::{Input, Level, Output, Pull, Speed};
use stm32::spi::Spi;
use stm32::time::Hertz;
use stm32::{pac, spi};
use uom::si::electric_potential::volt;
use uom::si::f32;
use defmt::{debug, error, info, trace, unwrap};
use static_cell::StaticCell;
use embassy_stm32::peripherals::{PA1, PA3, SPI1};
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use embassy_sync::pubsub::PubSubChannel;
use physical_node::transducer::{Publish, StatefulPublisher};
const AUTOCAL_CONF: Config = Config {
status: Status::setting(Buffer::Enabled, AutoCal::Enabled, BitOrder::MostSigFirst),
multiplexer: Multiplexer::setting(MuxInput::AIn0, MuxInput::Common),
ad_control: AdControl::setting(Gain::X2, Sdcs::Off, ClockOut::Off),
data_rate: DataRate::Sps2_5,
digital_io: DigitalIo::setting(DigitalIoState::default(), DigitalIoDirection::default()),
};
struct Ads1256Delay;
impl ads1256::BlockingDelay for Ads1256Delay {
#[inline]
fn t6_delay(&mut self) {
Delay.delay_us(1u32);
}
fn t11_1_delay(&mut self) {}
fn t11_2_delay(&mut self) {}
}
struct Inputs {
ai0: StatefulPublisher<f32::ElectricPotential, NoopRawMutex, 10, 1>,
ai1: StatefulPublisher<f32::ElectricPotential, NoopRawMutex, 10, 1>,
ai2: StatefulPublisher<f32::ElectricPotential, NoopRawMutex, 10, 1>,
}
// Inputs
static ANALOG_INPUTS: StaticCell<Inputs> = StaticCell::new();
static ADS_1256: StaticCell<Ads1256<Ads1256Delay, Output<PA1>, ExtiInput<PA3>>> = StaticCell::new();
static SPI: StaticCell<Spi<SPI1, NoDma, NoDma>> = StaticCell::new();
#[embassy_executor::main]
async fn main(spawner: Spawner) {
unsafe {
pac::FLASH.acr().modify(|v| {
v.set_prften(true);
v.set_icen(true);
v.set_dcen(true);
});
}
let p = embassy_stm32::init(Default::default());
let mut spi_conf = spi::Config::default();
spi_conf.mode = spi::MODE_1;
spi_conf.bit_order = spi::BitOrder::MsbFirst;
spi_conf.frequency = Hertz(ads1256::defaults::SPI_CLK_HZ);
let ads1256_data_ready = ExtiInput::new(Input::new(p.PA3, Pull::Up), p.EXTI3);
let select_ads1256 = Output::new(p.PA1, Level::High, Speed::VeryHigh);
let spi = SPI.init(Spi::new(
p.SPI1,
p.PA5,
p.PA7,
p.PA6,
NoDma,
NoDma,
spi_conf,
));
let ads_1256 = ADS_1256.init(Ads1256::new(Ads1256Delay, select_ads1256, ads1256_data_ready));
ads_1256.write_config(spi, AUTOCAL_CONF).unwrap();
let inputs = &*ANALOG_INPUTS.init(Inputs {
ai0: StatefulPublisher::new(
Cell::new(f32::ElectricPotential::new::<volt>(f32::NAN)).into(),
PubSubChannel::new().into(),
),
ai1: StatefulPublisher::new(
Cell::new(f32::ElectricPotential::new::<volt>(f32::NAN)).into(),
PubSubChannel::new().into(),
),
ai2: StatefulPublisher::new(
Cell::new(f32::ElectricPotential::new::<volt>(f32::NAN)).into(),
PubSubChannel::new().into(),
),
});
spawner.spawn(log_task(inputs)).unwrap();
spawner
.spawn(drive_inputs_task(ads_1256, spi, inputs))
.unwrap();
}
#[embassy_executor::task]
async fn drive_inputs_task(
ads_1256: &'static mut Ads1256<Ads1256Delay, Output<'static, PA1>, ExtiInput<'static, PA3>>,
spi: &'static mut Spi<'static, SPI1, NoDma, NoDma>,
inputs: &'static Inputs,
) {
let Inputs { ai0, ai1, ai2 } = inputs;
loop {
let mut accumulator = f32::ElectricPotential::new::<volt>(0.0);
let voltage = ads_1256
.autocal_convert(spi, SingleEnded::AIn0.into(), None, None, None, false)
.await
.unwrap()
.to_voltage(AUTOCAL_CONF.ad_control.gain());
ai0.update(voltage);
accumulator += voltage;
let voltage = ads_1256
.autocal_convert(spi, SingleEnded::AIn1.into(), None, None, None, false)
.await
.unwrap()
.to_voltage(AUTOCAL_CONF.ad_control.gain());
ai1.update(voltage);
accumulator += voltage;
let voltage = ads_1256
.autocal_convert(spi, SingleEnded::AIn2.into(), None, None, None, false)
.await
.unwrap()
.to_voltage(AUTOCAL_CONF.ad_control.gain());
ai2.update(voltage);
accumulator += voltage;
let accum_volts = accumulator.get::<volt>();
info!("Immediate loop iteration result, combined volts: {}", accum_volts);
}
}
#[embassy_executor::task]
async fn log_task(inputs: &'static Inputs) {
let Inputs { ai0, ai1, ai2 } = inputs;
let mut ai0_sub = ai0.subscribe().unwrap();
let mut ai1_sub = ai1.subscribe().unwrap();
let mut ai2_sub = ai2.subscribe().unwrap();
loop {
let msg = ai0_sub.next_message_pure().await.get::<volt>();
info!("Log task ai0: {}", msg);
let msg = ai1_sub.next_message_pure().await.get::<volt>();
info!("Log task ai1: {}", msg);
let msg = ai2_sub.next_message_pure().await.get::<volt>();
info!("Log task ai2: {}", msg);
}
}

View File

@ -1,195 +0,0 @@
#![no_std]
#![no_main]
use core::cell::Cell;
use cortex_m::prelude::_embedded_hal_blocking_delay_DelayUs;
use {defmt_rtt as _, panic_probe as _};
use {embassy_executor as executor, embassy_stm32 as stm32};
use ads1256::standard::input::SingleEnded;
use ads1256::{
AdControl, Ads1256, AutoCal, BitOrder, Buffer, ClockOut, Config, DState, DataRate, DigitalIo,
DigitalIoDirection, DigitalIoState, Gain, Multiplexer, MuxInput, Sdcs, Status,
};
use embassy_time::{Delay, Duration, Timer};
use executor::Spawner;
use stm32::dma::NoDma;
use stm32::exti::ExtiInput;
use stm32::gpio::{Input, Level, Output, Pull, Speed};
use stm32::spi::Spi;
use stm32::time::Hertz;
use stm32::{pac, spi};
use uom::si::electric_potential::volt;
use uom::si::f32;
use defmt::info;
use static_cell::StaticCell;
use embassy_stm32::peripherals::{PA1, PA3, SPI1};
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
use embassy_sync::mutex::Mutex;
use embassy_sync::pubsub::PubSubChannel;
use physical_ads1256::standard::multiplexer::poll::{
AutocalPoll, AutocalPollStatePub, ModInputOnly,
};
use physical_node::transducer::input::Poll;
use physical_node::transducer::{Publish, Publisher, StatefulPublisher};
const AUTOCAL_CONF: Config = Config {
status: Status::setting(Buffer::Enabled, AutoCal::Enabled, BitOrder::MostSigFirst),
multiplexer: Multiplexer::setting(MuxInput::AIn0, MuxInput::Common),
ad_control: AdControl::setting(Gain::X2, Sdcs::Off, ClockOut::Off),
data_rate: DataRate::Sps2_5,
digital_io: DigitalIo::setting(DigitalIoState::default(), DigitalIoDirection::default()),
};
struct Ads1256Delay;
impl ads1256::BlockingDelay for Ads1256Delay {
#[inline]
fn t6_delay(&mut self) {
Delay.delay_us(1u32);
}
fn t11_1_delay(&mut self) {}
fn t11_2_delay(&mut self) {}
}
type ExampleInput = AutocalPollStatePub<
'static,
NoopRawMutex,
NoopRawMutex,
ModInputOnly,
Ads1256Delay,
Output<'static, PA1>,
ExtiInput<'static, PA3>,
Spi<'static, SPI1, NoDma, NoDma>,
10,
1,
>;
struct Inputs {
ai1: ExampleInput,
ai2: ExampleInput,
ai3: ExampleInput,
}
// Inputs
static ANALOG_INPUTS: StaticCell<Inputs> = StaticCell::new();
static ADS_1256: StaticCell<
Mutex<NoopRawMutex, Ads1256<Ads1256Delay, Output<PA1>, ExtiInput<PA3>>>,
> = StaticCell::new();
static SPI: StaticCell<Mutex<NoopRawMutex, Spi<SPI1, NoDma, NoDma>>> = StaticCell::new();
#[embassy_executor::main]
async fn main(spawner: Spawner) {
unsafe {
pac::FLASH.acr().modify(|v| {
v.set_prften(true);
v.set_icen(true);
v.set_dcen(true);
});
}
let p = embassy_stm32::init(Default::default());
let mut spi_conf = spi::Config::default();
spi_conf.mode = spi::MODE_1;
spi_conf.bit_order = spi::BitOrder::MsbFirst;
spi_conf.frequency = Hertz(ads1256::defaults::SPI_CLK_HZ);
let ads1256_data_ready = ExtiInput::new(Input::new(p.PA3, Pull::Up), p.EXTI3);
let select_ads1256 = Output::new(p.PA1, Level::High, Speed::VeryHigh);
let spi = SPI.init(Mutex::new(
Spi::new(
p.SPI1,
p.PA5,
p.PA7,
p.PA6,
NoDma,
NoDma,
spi_conf,
)
));
let ads_1256 =
ADS_1256.init(Mutex::new(Ads1256::new(Ads1256Delay, select_ads1256, ads1256_data_ready)));
ads_1256
.get_mut()
.write_config(spi.get_mut(), AUTOCAL_CONF)
.unwrap();
let inputs = &*ANALOG_INPUTS.init(Inputs {
ai1: AutocalPollStatePub {
poll: AutocalPoll::new(
ModInputOnly {
gain: Gain::X2,
multiplexer: SingleEnded::AIn1.into(),
},
ads_1256,
spi,
),
publisher: PubSubChannel::new().into(),
state: Cell::new(f32::ElectricPotential::new::<volt>(f32::NAN)).into(),
},
ai2: AutocalPollStatePub {
poll: AutocalPoll::new(
ModInputOnly {
gain: Gain::X2,
multiplexer: SingleEnded::AIn2.into(),
},
ads_1256,
spi,
),
publisher: PubSubChannel::new().into(),
state: Cell::new(f32::ElectricPotential::new::<volt>(f32::NAN)).into(),
},
ai3: AutocalPollStatePub {
poll: AutocalPoll::new(
ModInputOnly {
gain: Gain::X2,
multiplexer: SingleEnded::AIn3.into(),
},
ads_1256,
spi,
),
publisher: PubSubChannel::new().into(),
state: Cell::new(f32::ElectricPotential::new::<volt>(f32::NAN)).into(),
},
});
spawner.spawn(log_task(&inputs.ai1, 1)).unwrap();
spawner.spawn(log_task(&inputs.ai2, 2)).unwrap();
spawner.spawn(log_task(&inputs.ai3, 3)).unwrap();
spawner
.spawn(poll_task(&inputs.ai1, 1, Duration::from_secs(5)))
.unwrap();
spawner
.spawn(poll_task(&inputs.ai2, 2, Duration::from_secs(10)))
.unwrap();
spawner
.spawn(poll_task(&inputs.ai3, 3, Duration::from_secs(20)))
.unwrap();
}
#[embassy_executor::task(pool_size = 3)]
async fn poll_task(input: &'static ExampleInput, input_num: u8, every: Duration) {
loop {
Timer::after(every).await;
let result = input.poll().await.unwrap().get::<volt>();
info!("ai{} poll result: {} volts", input_num, result);
}
}
#[embassy_executor::task(pool_size = 3)]
async fn log_task(input: &'static ExampleInput, input_num: u8) {
let mut subscriber = input.subscribe().unwrap();
loop {
let msg = subscriber.next_message_pure().await.get::<volt>();
info!("Log task ai{}: {}", input_num, msg);
}
}

View File

@ -1,125 +0,0 @@
#![no_std]
#![no_main]
use cortex_m::prelude::_embedded_hal_blocking_delay_DelayUs;
// Necessary unused import.
use {defmt_rtt as _, panic_probe as _};
use {embassy_executor as executor, embassy_stm32 as stm32};
use ads1256::standard::input::{Differential, SingleEnded};
use ads1256::{
AdControl, Ads1256, AutoCal, BitOrder, Buffer, ClockOut, Config, DataRate, DigitalIo,
DigitalIoDirection, DigitalIoState, Gain, Multiplexer, MuxInput, Sdcs, Status,
};
use embassy_time::Delay;
use executor::Spawner;
use stm32::dma::NoDma;
use stm32::exti::ExtiInput;
use stm32::gpio::{Input, Level, Output, Pull, Speed};
use stm32::spi::Spi;
use stm32::time::Hertz;
use stm32::{pac, spi};
use uom::si::electric_potential::{millivolt, volt};
use defmt::info;
use physical::transducer::{lm35, thermocouple_k};
use uom::si::thermodynamic_temperature::degree_celsius;
const AUTOCAL_CONF: Config = Config {
status: Status::setting(Buffer::Enabled, AutoCal::Enabled, BitOrder::MostSigFirst),
multiplexer: Multiplexer::setting(MuxInput::AIn0, MuxInput::AIn1),
ad_control: AdControl::setting(Gain::X64, Sdcs::Off, ClockOut::Off),
data_rate: DataRate::Sps2_5,
digital_io: DigitalIo::setting(DigitalIoState::default(), DigitalIoDirection::default()),
};
struct Ads1256Delay;
impl ads1256::BlockingDelay for Ads1256Delay {
#[inline]
fn t6_delay(&mut self) {
Delay.delay_us(1u32);
}
fn t11_1_delay(&mut self) {}
fn t11_2_delay(&mut self) {}
}
#[embassy_executor::main]
async fn main(spawner: Spawner) {
unsafe {
pac::FLASH.acr().modify(|v| {
v.set_prften(true);
v.set_icen(true);
v.set_dcen(true);
});
}
let p = embassy_stm32::init(Default::default());
let mut spi_conf = spi::Config::default();
spi_conf.mode = spi::MODE_1;
spi_conf.bit_order = spi::BitOrder::MsbFirst;
spi_conf.frequency = Hertz(ads1256::defaults::SPI_CLK_HZ);
let ads1256_data_ready = ExtiInput::new(Input::new(p.PA3, Pull::Up), p.EXTI3);
let select_ads1256 = Output::new(p.PA1, Level::High, Speed::VeryHigh);
let mut spi = Spi::new(
p.SPI1,
p.PA5,
p.PA7,
p.PA6,
NoDma,
NoDma,
spi_conf,
);
let mut ads_1256 = Ads1256::new(Ads1256Delay, select_ads1256, ads1256_data_ready);
ads_1256.write_config(&mut spi, AUTOCAL_CONF).unwrap();
ads_1256.self_calibrate(&mut spi).await.unwrap();
loop {
let gain = Gain::X2;
let reference = ads_1256
.autocal_convert(
&mut spi,
SingleEnded::AIn2.into(),
None,
Some(AUTOCAL_CONF.ad_control.with_gain(gain)),
None,
false,
)
.await
.unwrap()
.to_voltage(gain);
let reference_volts = reference.get::<volt>();
let reference = lm35::convert(reference).unwrap();
let reference_celsius = reference.get::<degree_celsius>();
info!(
"Reference junction temperature: {}°C, from voltage reading: {}V",
reference_celsius, reference_volts
);
let gain = Gain::X64;
let voltage = ads_1256
.autocal_convert(
&mut spi,
Differential::AIn0.into(),
None,
Some(AUTOCAL_CONF.ad_control.with_gain(gain)),
None,
false,
)
.await
.unwrap()
.to_voltage(AUTOCAL_CONF.ad_control.gain());
let mv = voltage.get::<millivolt>();
let temperature = thermocouple_k::convert_direct(voltage, reference).unwrap();
let celsius = temperature.get::<degree_celsius>();
info!("Thermocouple temperature: {}°C, from reading: {}mV", celsius, mv);
}
}

View File

@ -1,28 +0,0 @@
[package]
name = "node-poll-variants"
description = "Macros for physical nodes."
version.workspace = true
edition.workspace = true
repository.workspace = true
readme.workspace = true
license.workspace = true
[lib]
proc-macro = true
[[test]]
name = "tests"
path = "tests/test_build.rs"
[dependencies.syn]
workspace = true
[dependencies.quote]
workspace = true
[dev-dependencies.trybuild]
workspace = true
[dev-dependencies.physical-node]
path = "../../node"
features = ["embassy-sync"]
[dev-dependencies.embassy-sync]
workspace = true

View File

@ -1,294 +0,0 @@
use proc_macro::TokenStream;
use quote::quote;
use std::ops::Deref;
use quote::__private::parse_spanned;
use syn::__private::{str, Span};
use syn::punctuated::Punctuated;
use syn::token::{Colon, Comma, PathSep, Plus};
use syn::{parse_macro_input, parse_quote, parse_quote_spanned, parse_str, Data, DeriveInput, Expr, GenericParam, Generics, Ident, Lit, LitStr, Meta, Path, PathSegment, Token, TraitBound, TraitBoundModifier, TypeParam, TypeParamBound, Type};
#[proc_macro_derive(PollVariants, attributes(value_type, error_type))]
pub fn poll_variant_macro(input: TokenStream) -> TokenStream {
// ----- Parse input ----------------------------------
let input = parse_macro_input!(input as DeriveInput);
let attrs = &input.attrs;
let vis = &input.vis;
let ident = &input.ident;
let data = &input.data;
let og_generics = &input.generics;
let (og_impl_generics, og_type_generics, og_where_clause) = &og_generics.split_for_impl();
// ----- Check that item the macro was used on is a struct ----------------------------------
match data {
Data::Struct(struct_data) => struct_data,
Data::Enum(_) => panic!("Stateful struct cannot be derived from an enum."),
Data::Union(_) => panic!("Stateful struct cannot be derived from a union."),
};
// ----- Extract attribute information ----------------------------------
const VALUE_T_NAME: &str = "value_type";
const ERROR_T_NAME: &str = "error_type";
let mut value_type: Option<Type> = None;
let mut error_type: Option<Type> = None;
for attribute in attrs.iter() {
// if the attribute is a named value
if let Meta::NameValue(meta) = &attribute.meta {
// if the name of the attribute is value_type
if meta.path.segments[0].ident == VALUE_T_NAME {
// if the value of the attribute is a literal
if let Expr::Lit(lit) = &meta.value {
// if the literal is a string
if let Lit::Str(lit) = &lit.lit {
let span = lit.span();
let string = lit.token().to_string();
let string = string.trim_matches('"').to_string();
let _value_type: Type = parse_str(string.deref()).unwrap();
let _value_type: Type = parse_quote_spanned!(span=> #_value_type);
value_type = Some(_value_type);
} else {
panic!("{VALUE_T_NAME} must be set with a string literal.")
}
} else {
panic!("{VALUE_T_NAME} must be set with a literal.")
}
}
// if the name of the attribute is error_type
if meta.path.segments[0].ident == ERROR_T_NAME {
// if the value of the attribute is a literal
if let Expr::Lit(lit) = &meta.value {
// if the literal is a string
if let Lit::Str(lit) = &lit.lit {
let span = lit.span();
let string = lit.token().to_string();
let string = string.trim_matches('"').to_string();
let _error_type: Type = parse_str(string.deref()).unwrap();
let _error_type: Type = parse_quote_spanned!(span=> #_error_type);
error_type = Some(_error_type);
} else {
panic!("{ERROR_T_NAME} must be set with a string literal.")
}
} else {
panic!("{ERROR_T_NAME} must be set with a literal.")
}
}
}
}
let value_type = value_type
.expect(format!("Need attribute {VALUE_T_NAME}: #[{VALUE_T_NAME} = \"type\"]").deref());
let error_type = error_type
.expect(format!("Need attribute {ERROR_T_NAME}: #[{ERROR_T_NAME} = \"type\"]").deref());
// ----- Add publisher generics ----------------------------------
// MutexT
const MUTEX_T_NAME: &str = "PublishMutexT";
let mutex_t_ident = Ident::new(MUTEX_T_NAME, Span::call_site());
const CAPACITY_NAME: &str = "CAPACITY";
let capacity_ident = Ident::new(CAPACITY_NAME, Span::call_site());
const NUM_SUBS_NAME: &str = "NUM_SUBS";
let num_subs_ident = Ident::new(NUM_SUBS_NAME, Span::call_site());
let mut num_lifetimes: usize = 0;
let mut num_types: usize = 0;
let mut num_const: usize = 0;
let mut has_mutex_t = false;
for param in og_generics.params.iter() {
match param {
GenericParam::Lifetime(_) => num_lifetimes += 1,
GenericParam::Type(param) => {
num_types += 1;
// If the generic parameter is MutexT
if param.ident == MUTEX_T_NAME {
has_mutex_t = true;
}
},
GenericParam::Const(_) => num_const += 1,
}
}
let mut publish_generics = og_generics.clone();
// If MutexT is not a generic parameter, add it
if !has_mutex_t {
let mutex_t_param: GenericParam =
parse_quote!(#mutex_t_ident: embassy_sync::blocking_mutex::raw::RawMutex);
let num_generics = num_lifetimes + num_types + num_const;
// If there are generics
if num_generics > 0 {
// If all generics are lifetimes
if num_lifetimes == num_generics {
// Add MutexT after the lifetimes
publish_generics.params.push(mutex_t_param);
// If no generics are lifetimes
} else if num_lifetimes == 0 {
// Insert MutexT at the front
publish_generics.params.insert(0, mutex_t_param);
// If there are lifetimes and other generics
} else {
// Insert MutexT after the lifetimes
publish_generics.params.insert(num_lifetimes, mutex_t_param);
}
// If there are no generics
} else {
// Add MutexT
publish_generics.params.push(mutex_t_param);
}
}
// const generics
let capacity_param: GenericParam = parse_quote!(const #capacity_ident: usize);
let num_subs_param: GenericParam = parse_quote!(const #num_subs_ident: usize);
publish_generics.params.push(capacity_param);
publish_generics.params.push(num_subs_param);
let (publ_impl_generics, publ_type_generics, publ_where_clause) =
&publish_generics.split_for_impl();
let pubsub_error_path: Path = parse_quote!(embassy_sync::pubsub::Error);
let pubsub_sub_path: Path = parse_quote!(embassy_sync::pubsub::Subscriber);
let stateful_variant_ident = Ident::new(format!("{ident}Stateful").deref(), ident.span());
let publish_variant_ident = Ident::new(format!("{ident}Publish").deref(), ident.span());
let state_pub_variant_ident = Ident::new(format!("{ident}StatePub").deref(), ident.span());
let poll_path: Path = parse_quote!(physical_node::transducer::input::Poll);
let stateful_path: Path = parse_quote!(physical_node::transducer::Stateful);
let publish_path: Path = parse_quote!(physical_node::transducer::Publish);
let state_path: Path = parse_quote!(physical_node::transducer::State);
let publisher_path: Path = parse_quote!(physical_node::transducer::Publisher);
let cellview_path: Path = parse_quote!(physical_node::cell::CellView);
let error_path: Path = parse_quote!(physical_node::CriticalError);
let expanded = quote! {
// ----- Stateful struct ----------------------------------
#vis struct #stateful_variant_ident #og_generics #og_where_clause {
pub poll: #ident #og_type_generics,
pub state: #state_path<#value_type>,
}
// ----- Stateful impls ----------------------------------
impl #og_impl_generics #poll_path for #stateful_variant_ident #og_type_generics #og_where_clause {
type Value = #value_type;
type Error = #error_type;
#[inline]
async fn poll(&self) -> Result<Self::Value, Self::Error> {
let result = self.poll.poll().await;
if let Ok(value) = result {
self.state.update(value);
}
result
}
}
impl #og_impl_generics #stateful_path for #stateful_variant_ident #og_type_generics #og_where_clause {
type Value = #value_type;
#[inline(always)]
fn state_cell(&self) -> #cellview_path<Self::Value> {
self.state.state_cell()
}
#[inline(always)]
fn state(&self) -> Self::Value {
self.state.state()
}
}
// ----- Publish struct ----------------------------------
#[cfg(feature = "embassy-sync")]
#vis struct #publish_variant_ident #publish_generics #publ_where_clause {
pub poll: #ident #og_type_generics,
pub publisher: #publisher_path<#value_type, #mutex_t_ident, #capacity_ident, #num_subs_ident>,
}
// ----- Publish impl ----------------------------------
#[cfg(feature = "embassy-sync")]
impl #publ_impl_generics #poll_path for #publish_variant_ident #publ_type_generics #publ_where_clause {
type Value = #value_type;
type Error = #error_type;
#[inline]
async fn poll(&self) -> Result<Self::Value, Self::Error> {
let result = self.poll.poll().await;
if let Ok(value) = result {
self.publisher.update(value);
}
result
}
}
#[cfg(feature = "embassy-sync")]
impl #publ_impl_generics #publish_path<#capacity_ident, #num_subs_ident> for #publish_variant_ident #publ_type_generics #publ_where_clause {
type Value = #value_type;
type Mutex = #mutex_t_ident;
#[inline(always)]
fn subscribe(
&self,
) -> Result<#pubsub_sub_path<Self::Mutex, Self::Value, #capacity_ident, #num_subs_ident, 0>, #pubsub_error_path> {
self.publisher.subscribe()
}
}
// ----- StatePub struct ----------------------------------
#[cfg(feature = "embassy-sync")]
#vis struct #state_pub_variant_ident #publish_generics #publ_where_clause {
pub poll: #ident #og_type_generics,
pub state: #state_path<#value_type>,
pub publisher: #publisher_path<#value_type, #mutex_t_ident, #capacity_ident, #num_subs_ident>,
}
#[cfg(feature = "embassy-sync")]
impl #publ_impl_generics #poll_path for #state_pub_variant_ident #publ_type_generics #publ_where_clause {
type Value = #value_type;
type Error = #error_type;
#[inline]
async fn poll(&self) -> Result<Self::Value, Self::Error> {
let result = self.poll.poll().await;
if let Ok(value) = result {
self.state.update(value);
self.publisher.update(value);
}
result
}
}
#[cfg(feature = "embassy-sync")]
impl #publ_impl_generics #stateful_path for #state_pub_variant_ident #publ_type_generics #publ_where_clause {
type Value = #value_type;
#[inline(always)]
fn state_cell(&self) -> #cellview_path<Self::Value> {
self.state.state_cell()
}
#[inline(always)]
fn state(&self) -> Self::Value {
self.state.state()
}
}
#[cfg(feature = "embassy-sync")]
impl #publ_impl_generics #publish_path<#capacity_ident, #num_subs_ident> for #state_pub_variant_ident #publ_type_generics #publ_where_clause {
type Value = #value_type;
type Mutex = #mutex_t_ident;
#[inline(always)]
fn subscribe(
&self,
) -> Result<#pubsub_sub_path<Self::Mutex, Self::Value, #capacity_ident, #num_subs_ident, 0>, #pubsub_error_path> {
self.publisher.subscribe()
}
}
};
TokenStream::from(expanded)
}

View File

@ -1,31 +0,0 @@
#![feature(async_fn_in_trait, impl_trait_projections, never_type)]
use node_poll_variants::PollVariants;
use physical_node::transducer::input::Poll;
#[derive(PollVariants)]
#[value_type = "SecondT"]
#[error_type = "!"]
struct ExamplePoll<'a, FirstT, SecondT>
where
SecondT: Copy,
{
a: &'a i32,
b: i32,
first: FirstT,
second: SecondT,
}
impl<'a, FirstT, SecondT> Poll for ExamplePoll<'a, FirstT, SecondT>
where
SecondT: Copy,
{
type Value = SecondT;
type Error = !;
async fn poll(&self) -> Result<Self::Value, Self::Error> {
Ok(self.second)
}
}
fn main() {}

View File

@ -1,5 +0,0 @@
#[test]
fn tests() {
let t = trybuild::TestCases::new();
t.pass("tests/generate.rs");
}

View File

@ -17,6 +17,3 @@ workspace = true
workspace = true
[dependencies.uom]
workspace = true
[dependencies.embassy-sync]
workspace = true
optional = true

View File

@ -1,9 +1,3 @@
#![no_std]
pub mod transducer;
pub mod cell {
pub use physical::cell::*;
}
pub use physical::CriticalError;
pub use physical::CriticalError;

View File

@ -1 +0,0 @@
pub use physical::transducer::input::*;

View File

@ -1,8 +0,0 @@
pub mod input;
pub mod output;
#[cfg(feature = "embassy-sync")]
mod sync;
pub use physical::transducer::*;
#[cfg(feature = "embassy-sync")]
pub use sync::*;

View File

@ -1 +0,0 @@
pub use physical::transducer::output::*;

View File

@ -1,61 +0,0 @@
use crate::cell::CellView;
use crate::transducer::{Publish, Publisher};
use core::cell::Cell;
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_sync::pubsub::{Error, PubSubChannel, Subscriber};
use physical::transducer::{State, Stateful};
pub struct StatefulPublisher<
T: Copy,
MutexT: RawMutex,
const CAPACITY: usize,
const NUM_SUBS: usize,
> {
pub state: State<T>,
pub publisher: Publisher<T, MutexT, CAPACITY, NUM_SUBS>,
}
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize>
StatefulPublisher<T, MutexT, CAPACITY, NUM_SUBS>
{
#[inline(always)]
pub fn new(state: State<T>, publisher: Publisher<T, MutexT, CAPACITY, NUM_SUBS>) -> Self {
Self { state, publisher }
}
#[inline(always)]
pub fn update(&self, value: T) {
self.state.update(value);
self.publisher.update(value);
}
}
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize> Stateful
for StatefulPublisher<T, MutexT, CAPACITY, NUM_SUBS>
{
type Value = T;
#[inline(always)]
fn state_cell(&self) -> CellView<Self::Value> {
self.state.state_cell()
}
#[inline(always)]
fn state(&self) -> Self::Value {
self.state.state()
}
}
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize>
Publish<CAPACITY, NUM_SUBS> for StatefulPublisher<T, MutexT, CAPACITY, NUM_SUBS>
{
type Value = T;
type Mutex = MutexT;
#[inline(always)]
fn subscribe(
&self,
) -> Result<Subscriber<Self::Mutex, Self::Value, CAPACITY, NUM_SUBS, 0>, Error> {
self.publisher.subscribe()
}
}

View File

@ -1,58 +0,0 @@
mod input;
pub use input::*;
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_sync::pubsub;
use embassy_sync::pubsub::{PubSubBehavior, PubSubChannel, Subscriber};
pub trait Publish<const CAPACITY: usize, const NUM_SUBS: usize> {
type Value: Copy;
type Mutex: RawMutex;
fn subscribe(
&self,
) -> Result<Subscriber<Self::Mutex, Self::Value, CAPACITY, NUM_SUBS, 0>, pubsub::Error>;
}
pub struct Publisher<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize> {
channel: PubSubChannel<MutexT, T, CAPACITY, NUM_SUBS, 0>,
}
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize>
Publisher<T, MutexT, CAPACITY, NUM_SUBS>
{
#[inline(always)]
pub fn new(channel: PubSubChannel<MutexT, T, CAPACITY, NUM_SUBS, 0>) -> Self {
Self { channel }
}
#[inline(always)]
pub fn update(&self, value: T) {
self.channel.publish_immediate(value);
}
}
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize>
Publish<CAPACITY, NUM_SUBS> for Publisher<T, MutexT, CAPACITY, NUM_SUBS>
{
type Value = T;
type Mutex = MutexT;
#[inline(always)]
fn subscribe(
&self,
) -> Result<Subscriber<Self::Mutex, Self::Value, CAPACITY, NUM_SUBS, 0>, pubsub::Error> {
self.channel.subscriber()
}
}
impl<T: Copy, MutexT: RawMutex, const CAPACITY: usize, const NUM_SUBS: usize>
From<PubSubChannel<MutexT, T, CAPACITY, NUM_SUBS, 0>>
for Publisher<T, MutexT, CAPACITY, NUM_SUBS>
{
#[inline(always)]
fn from(channel: PubSubChannel<MutexT, T, CAPACITY, NUM_SUBS, 0>) -> Self {
Publisher::new(channel)
}
}

View File

@ -1,31 +0,0 @@
[package]
name = "physical-ads1256"
description = "Shared node abstractions for ADS1256 components."
version.workspace = true
edition.workspace = true
repository.workspace = true
readme.workspace = true
license.workspace = true
[features]
embassy-sync = ["dep:embassy-sync", "ads1256/embassy-sync", "physical-node/embassy-sync"]
poll = ["standard-multiplexer", "embassy-sync"]
standard-multiplexer = []
[dependencies.physical-node]
path = "../../../node"
[dependencies.node-poll-variants]
path = "../../../macros/node-poll-variants"
[dependencies.ads1256]
workspace = true
[dependencies.embedded-hal]
workspace = true
[dependencies.embedded-hal-async]
workspace = true
[dependencies.defmt]
workspace = true
[dependencies.uom]
workspace = true
[dependencies.embassy-sync]
workspace = true
optional = true

View File

@ -1,3 +0,0 @@
#![no_std]
pub mod standard;

View File

@ -1,2 +0,0 @@
#[cfg(feature = "standard-multiplexer")]
pub mod multiplexer;

View File

@ -1,4 +0,0 @@
mod sync;
#[cfg(feature = "embassy-sync")]
pub use sync::*;

View File

@ -1,2 +0,0 @@
#[cfg(feature = "poll")]
pub mod poll;

View File

@ -1,382 +0,0 @@
use ads1256::{
AdControl, Ads1256, BlockingDelay, DataRate, Gain, Multiplexer, OutputPin, SpiBus, Status, Wait,
};
use core::ops::DerefMut;
use embassy_sync::blocking_mutex::raw::RawMutex;
use embassy_sync::mutex::Mutex;
use node_poll_variants::PollVariants;
use physical_node::transducer::input::Poll;
use physical_node::CriticalError;
use uom::si::f32;
#[derive(PollVariants)]
#[value_type = "f32::ElectricPotential"]
#[error_type = "CriticalError"]
pub struct AutocalPoll<'a, DeviceMutexT, ModInT, DelayerT, SST, DrdyT, SpiT>
where
DeviceMutexT: RawMutex,
ModInT: ModInput,
DelayerT: BlockingDelay,
SST: OutputPin,
DrdyT: Wait,
SpiT: SpiBus,
{
input_mod: ModInT,
ads1256: &'a Mutex<DeviceMutexT, Ads1256<DelayerT, SST, DrdyT>>,
spi: &'a Mutex<DeviceMutexT, SpiT>,
}
impl<'a, DeviceMutexT, ModInT, DelayerT, SST, DrdyT, SpiT>
AutocalPoll<'a, DeviceMutexT, ModInT, DelayerT, SST, DrdyT, SpiT>
where
DeviceMutexT: RawMutex,
ModInT: ModInput,
DelayerT: BlockingDelay,
SST: OutputPin,
DrdyT: Wait,
SpiT: SpiBus,
{
#[inline(always)]
pub fn new(
input_mod: ModInT,
ads1256: &'a Mutex<DeviceMutexT, Ads1256<DelayerT, SST, DrdyT>>,
spi: &'a Mutex<DeviceMutexT, SpiT>,
) -> Self {
Self {
input_mod,
ads1256,
spi,
}
}
}
impl<'a, DeviceMutexT, ModInT, DelayerT, SST, DrdyT, SpiT> Poll
for AutocalPoll<'a, DeviceMutexT, ModInT, DelayerT, SST, DrdyT, SpiT>
where
DeviceMutexT: RawMutex,
ModInT: ModInput,
DelayerT: BlockingDelay,
SST: OutputPin,
DrdyT: Wait,
SpiT: SpiBus,
{
type Value = f32::ElectricPotential;
type Error = CriticalError;
async fn poll(&self) -> Result<Self::Value, CriticalError> {
let mut ads1256_guard = self.ads1256.lock().await;
let ads1256 = ads1256_guard.deref_mut();
//TODO: ADS1256 documentation seems to say we should be waiting for drdy low after
// issuing standby command but it never goes low.
let result = ads1256
.autocal_convert_m(
self.spi,
self.input_mod.multiplexer(),
self.input_mod.status(),
self.input_mod.ad_control(),
self.input_mod.data_rate(),
true,
)
.await;
match result {
Ok(conversion) => Ok(conversion.to_voltage(self.input_mod.gain())),
Err(_) => Err(CriticalError::Communication),
}
}
}
pub trait ModInput: Copy {
fn multiplexer(self) -> Multiplexer;
fn gain(self) -> Gain;
fn status(self) -> Option<Status>;
fn ad_control(self) -> Option<AdControl>;
fn data_rate(self) -> Option<DataRate>;
}
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct ModInputOnly {
pub multiplexer: Multiplexer,
/// Only used for converting to voltage, this value must match what is set on the ADS1256, it
/// will not *be* set unlike the other fields.
pub gain: Gain,
}
impl ModInput for ModInputOnly {
fn multiplexer(self) -> Multiplexer {
self.multiplexer
}
fn gain(self) -> Gain {
self.gain
}
fn status(self) -> Option<Status> {
None
}
fn ad_control(self) -> Option<AdControl> {
None
}
fn data_rate(self) -> Option<DataRate> {
None
}
}
/// buffer
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct ModInStatus {
pub multiplexer: Multiplexer,
/// Only used for converting to voltage, this value must match what is set on the ADS1256, it
/// will not *be* set unlike the other fields.
pub gain: Gain,
pub status: Status,
}
impl ModInput for ModInStatus {
#[inline(always)]
fn multiplexer(self) -> Multiplexer {
self.multiplexer
}
#[inline(always)]
fn gain(self) -> Gain {
self.gain
}
#[inline(always)]
fn status(self) -> Option<Status> {
Some(self.status)
}
#[inline(always)]
fn ad_control(self) -> Option<AdControl> {
None
}
#[inline(always)]
fn data_rate(self) -> Option<DataRate> {
None
}
}
/// gain
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct ModInAdControl {
pub multiplexer: Multiplexer,
pub ad_control: AdControl,
}
impl ModInput for ModInAdControl {
#[inline(always)]
fn multiplexer(self) -> Multiplexer {
self.multiplexer
}
#[inline(always)]
fn gain(self) -> Gain {
self.ad_control.gain()
}
#[inline(always)]
fn status(self) -> Option<Status> {
None
}
#[inline(always)]
fn ad_control(self) -> Option<AdControl> {
Some(self.ad_control)
}
#[inline(always)]
fn data_rate(self) -> Option<DataRate> {
None
}
}
/// data rate
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct ModInDataRate {
pub multiplexer: Multiplexer,
/// Only used for converting to voltage, this value must match what is set on the ADS1256, it
/// will not *be* set unlike the other fields.
pub gain: Gain,
pub data_rate: DataRate,
}
impl ModInput for ModInDataRate {
#[inline(always)]
fn multiplexer(self) -> Multiplexer {
self.multiplexer
}
#[inline(always)]
fn gain(self) -> Gain {
self.gain
}
#[inline(always)]
fn status(self) -> Option<Status> {
None
}
#[inline(always)]
fn ad_control(self) -> Option<AdControl> {
None
}
#[inline(always)]
fn data_rate(self) -> Option<DataRate> {
Some(self.data_rate)
}
}
/// buffer, gain
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct ModInStatAdc {
pub multiplexer: Multiplexer,
pub status: Status,
pub ad_control: AdControl,
}
impl ModInput for ModInStatAdc {
#[inline(always)]
fn multiplexer(self) -> Multiplexer {
self.multiplexer
}
#[inline(always)]
fn gain(self) -> Gain {
self.ad_control.gain()
}
#[inline(always)]
fn status(self) -> Option<Status> {
Some(self.status)
}
#[inline(always)]
fn ad_control(self) -> Option<AdControl> {
Some(self.ad_control)
}
#[inline(always)]
fn data_rate(self) -> Option<DataRate> {
None
}
}
/// buffer, data rate
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct ModInStatDrate {
pub multiplexer: Multiplexer,
/// Only used for converting to voltage, this value must match what is set on the ADS1256, it
/// will not *be* set unlike the other fields.
pub gain: Gain,
pub status: Status,
pub data_rate: DataRate,
}
impl ModInput for ModInStatDrate {
#[inline(always)]
fn multiplexer(self) -> Multiplexer {
self.multiplexer
}
#[inline(always)]
fn gain(self) -> Gain {
self.gain
}
#[inline(always)]
fn status(self) -> Option<Status> {
Some(self.status)
}
#[inline(always)]
fn ad_control(self) -> Option<AdControl> {
None
}
#[inline(always)]
fn data_rate(self) -> Option<DataRate> {
Some(self.data_rate)
}
}
// gain, data rate
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct ModInAdcDrate {
pub multiplexer: Multiplexer,
pub ad_control: AdControl,
pub data_rate: DataRate,
}
impl ModInput for ModInAdcDrate {
#[inline(always)]
fn multiplexer(self) -> Multiplexer {
self.multiplexer
}
#[inline(always)]
fn gain(self) -> Gain {
self.ad_control.gain()
}
#[inline(always)]
fn status(self) -> Option<Status> {
None
}
#[inline(always)]
fn ad_control(self) -> Option<AdControl> {
Some(self.ad_control)
}
#[inline(always)]
fn data_rate(self) -> Option<DataRate> {
Some(self.data_rate)
}
}
/// buffer, gain, data rate
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct ModInAll {
pub multiplexer: Multiplexer,
pub status: Status,
pub ad_control: AdControl,
pub data_rate: DataRate,
}
impl ModInput for ModInAll {
#[inline(always)]
fn multiplexer(self) -> Multiplexer {
self.multiplexer
}
#[inline(always)]
fn gain(self) -> Gain {
self.ad_control.gain()
}
#[inline(always)]
fn status(self) -> Option<Status> {
Some(self.status)
}
#[inline(always)]
fn ad_control(self) -> Option<AdControl> {
Some(self.ad_control)
}
#[inline(always)]
fn data_rate(self) -> Option<DataRate> {
Some(self.data_rate)
}
}

2
src/adc/mod.rs Normal file
View File

@ -0,0 +1,2 @@
#[cfg(feature = "stm32")]
pub mod stm32;

13
src/adc/stm32.rs Normal file
View File

@ -0,0 +1,13 @@
use uom::si::electric_potential::millivolt;
use uom::si::quantities::ElectricPotential;
pub fn reading_to_voltage(
reading: u16,
reference_voltage: ElectricPotential<f32>,
v_ref_int_scale: f32,
) -> ElectricPotential<f32> {
let reference_mv = reference_voltage.get::<millivolt>();
let reading = f32::from(reading);
ElectricPotential::new::<millivolt>(reading * v_ref_int_scale / reference_mv)
}

View File

@ -1,20 +0,0 @@
use core::cell::Cell;
/// Provides a view only reference to a [Cell].
/// Useful alternative to `&Cell` when an API wants to control where a [Cell]s value can be set.
#[derive(Copy, Clone)]
pub struct CellView<'a, T: Copy>(&'a Cell<T>);
impl<T: Copy> CellView<'_, T> {
#[inline(always)]
pub fn get(self) -> T {
self.0.get()
}
}
impl<'a, T: Copy> From<&'a Cell<T>> for CellView<'a, T> {
#[inline(always)]
fn from(value: &'a Cell<T>) -> Self {
CellView(value)
}
}

View File

@ -1,4 +1,6 @@
use crate::transducer::InvalidValue;
/// Indicates the transducer value is statically known to be impossible.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct InvalidValue;
/// An error that it is likely impossible to recover from. This error should only be created in
/// situations where attempts to recover have already been attempted and have failed. Error handling

View File

@ -2,7 +2,8 @@
pub mod transducer;
pub mod control;
pub mod cell;
pub mod error;
pub mod adc;
pub use error::CriticalError;

View File

@ -1,6 +0,0 @@
pub trait Poll {
type Value: Copy;
type Error: Copy;
async fn poll(&self) -> Result<Self::Value, Self::Error>;
}

View File

@ -1,62 +1,3 @@
use core::cell::Cell;
use crate::cell::CellView;
pub mod input;
pub mod output;
mod part;
pub use part::*;
// Initialisation will always be async and won't complete until a state is available for all
// stateful transducers.
pub trait Stateful {
type Value: Copy;
fn state_cell(&self) -> CellView<Self::Value>;
fn state(&self) -> Self::Value;
}
pub struct State<T: Copy> {
state_cell: Cell<T>,
}
impl<T: Copy> State<T> {
#[inline(always)]
pub fn new(state_cell: Cell<T>) -> Self {
Self { state_cell }
}
#[inline(always)]
pub fn update(&self, value: T) {
self.state_cell.set(value);
}
}
impl<T: Copy> Stateful for State<T> {
type Value = T;
#[inline(always)]
fn state_cell(&self) -> CellView<Self::Value> {
(&self.state_cell).into()
}
#[inline(always)]
fn state(&self) -> Self::Value {
self.state_cell.get()
}
}
impl<T: Copy> From<Cell<T>> for State<T> {
#[inline(always)]
fn from(state_cell: Cell<T>) -> Self {
State::new(state_cell)
}
}
// ---------------------------------------------------------------------------------------------------------------------
// ----- Error ------------------------
// ---------------------------------------------------------------------------------------------------------------------
/// Indicates the transducer value is statically known to be impossible.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct InvalidValue;
pub use part::*;

View File

@ -1,8 +0,0 @@
use crate::transducer::InvalidValue;
pub trait Output {
type Value: Copy;
//TODO: Should this be maybe async?
fn output(&mut self, setting: Self::Value) -> Result<(), InvalidValue>;
}

View File

@ -1,4 +1,4 @@
use crate::transducer::InvalidValue;
use crate::error::InvalidValue;
use uom::si::electric_potential::volt;
use uom::si::quantities::{ElectricPotential, ThermodynamicTemperature};
use uom::si::thermodynamic_temperature::degree_celsius;

View File

@ -1,4 +1,4 @@
use crate::transducer::InvalidValue;
use crate::error::InvalidValue;
use libm::powf;
use uom::si::electric_potential::millivolt;
use uom::si::quantities::{ElectricPotential, ThermodynamicTemperature};