use heapless::FnvIndexSet;
use heapless::Vec;
use strum::EnumCount;
use crate::apl::descriptors::error::Error;
const NODE_POWER_DESCRIPTOR_SIZE: usize = 2;
#[derive(Debug)]
pub struct NodePowerDescriptor(Vec<u8, NODE_POWER_DESCRIPTOR_SIZE>);
impl NodePowerDescriptor {
fn new(
current_power_mode: CurrentPowerMode,
available_power_sources: AvailablePowerSources,
current_power_source: CurrentPowerSource,
current_power_source_level: CurrentPowerSourceLevel,
) -> Result<Self, Error> {
let power_source = match current_power_source {
CurrentPowerSource::ConstantMainPower => AvailablePowerSourcesFlag::ConstantMainPower,
CurrentPowerSource::RechargeableBattery => {
AvailablePowerSourcesFlag::RechargeableBattery
}
CurrentPowerSource::DisposableBattery => AvailablePowerSourcesFlag::DisposableBattery,
};
if !available_power_sources.is_set(power_source) {
return Err(Error::CurrentPowerSourceNotAvailable);
}
let mut byte_1: u8 = 0;
byte_1 |= (current_power_mode as u8) << 4;
byte_1 |= available_power_sources.0 as u8;
let mut byte_2: u8 = 0;
byte_2 |= (current_power_source as u8) << 4;
byte_2 |= current_power_source_level as u8;
Ok(Self(Vec::from_slice(&[byte_1, byte_2]).unwrap()))
}
fn current_power_mode(&self) -> CurrentPowerMode {
let current_power_mode = self.0[0] >> 4;
current_power_mode.into()
}
fn available_power_sources(&self) -> AvailablePowerSources {
AvailablePowerSources(self.0[0] & 0b1111)
}
fn current_power_source(&self) -> CurrentPowerSource {
let current_power_source = self.0[1] >> 4;
current_power_source.into()
}
fn current_power_source_level(&self) -> CurrentPowerSourceLevel {
let current_power_source_level = self.0[1] & 0b1111;
current_power_source_level.into()
}
}
#[repr(u8)]
#[derive(Debug, PartialEq)]
pub enum CurrentPowerMode {
Synchronized = 0b0000,
Periodically = 0b0001,
Stimulated = 0b0010,
}
impl From<u8> for CurrentPowerMode {
fn from(value: u8) -> Self {
match value {
0b0000 => Self::Synchronized,
0b0001 => Self::Periodically,
0b0010 => Self::Stimulated,
_ => panic!("{}", "Invalid CurrentPowerMode value: {value}"),
}
}
}
pub struct AvailablePowerSources(u8);
#[repr(u8)]
#[derive(Clone, Copy, Eq, Hash, PartialEq, EnumCount)]
pub enum AvailablePowerSourcesFlag {
ConstantMainPower = 0,
RechargeableBattery = 1,
DisposableBattery = 2,
}
impl AvailablePowerSources {
fn new(
available_power_sources_flags: FnvIndexSet<
AvailablePowerSourcesFlag,
{ AvailablePowerSourcesFlag::COUNT.next_power_of_two() },
>,
) -> Self {
let mut value: u8 = 0;
for available_power_source in available_power_sources_flags.iter() {
value |= 1 << *available_power_source as u8;
}
Self(value)
}
fn is_set(&self, power_source: AvailablePowerSourcesFlag) -> bool {
return (self.0 & (1 << power_source as u8)) != 0;
}
}
#[repr(u8)]
#[derive(Debug, PartialEq)]
pub enum CurrentPowerSource {
ConstantMainPower = 0,
RechargeableBattery = 1,
DisposableBattery = 2,
}
impl From<u8> for CurrentPowerSource {
fn from(value: u8) -> Self {
match value {
0 => Self::ConstantMainPower,
1 => Self::RechargeableBattery,
2 => Self::DisposableBattery,
_ => panic!("{}", "Invalid CurrentPowerMode value: {value}"),
}
}
}
#[repr(u8)]
#[derive(Debug, PartialEq)]
pub enum CurrentPowerSourceLevel {
Critical = 0b0000,
OneThird = 0b0100,
TwoThirds = 0b1000,
Full = 0b1100,
}
impl From<u8> for CurrentPowerSourceLevel {
fn from(value: u8) -> Self {
match value {
0b0000 => Self::Critical,
0b0100 => Self::OneThird,
0b1000 => Self::TwoThirds,
0b1100 => Self::Full,
_ => panic!("{}", "Invalid CurrentPowerSourceLevel value: {value}"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::internal::types::macros::bitfield_bits;
#[test]
fn creating_available_power_sources_should_succeed() {
let expected: u8 = 0b0101;
let bits = bitfield_bits!(
AvailablePowerSourcesFlag;
AvailablePowerSourcesFlag::ConstantMainPower,
AvailablePowerSourcesFlag::DisposableBattery,
);
let available_power_sources = AvailablePowerSources::new(bits);
assert_eq!(expected, available_power_sources.0);
}
#[test]
fn reading_available_power_sources_should_succeed() {
let bits = bitfield_bits!(
AvailablePowerSourcesFlag;
AvailablePowerSourcesFlag::ConstantMainPower,
AvailablePowerSourcesFlag::DisposableBattery,
);
let available_power_sources = AvailablePowerSources::new(bits);
assert!(available_power_sources.is_set(AvailablePowerSourcesFlag::ConstantMainPower));
assert!(available_power_sources.is_set(AvailablePowerSourcesFlag::DisposableBattery));
assert!(!available_power_sources.is_set(AvailablePowerSourcesFlag::RechargeableBattery));
}
#[test]
fn creating_node_power_descriptor_should_succeed() {
let current_power_mode = CurrentPowerMode::Synchronized;
let available_power_sources_flags = bitfield_bits!(
AvailablePowerSourcesFlag;
AvailablePowerSourcesFlag::ConstantMainPower,
);
let available_power_sources = AvailablePowerSources::new(available_power_sources_flags);
let current_power_source = CurrentPowerSource::ConstantMainPower;
let current_power_source_level = CurrentPowerSourceLevel::TwoThirds;
let node_power_descriptor = NodePowerDescriptor::new(
current_power_mode,
available_power_sources,
current_power_source,
current_power_source_level,
);
assert!(node_power_descriptor.is_ok());
let node_power_descriptor = node_power_descriptor.unwrap();
assert_eq!(
node_power_descriptor.current_power_mode(),
CurrentPowerMode::Synchronized
);
assert!(node_power_descriptor
.available_power_sources()
.is_set(AvailablePowerSourcesFlag::ConstantMainPower));
assert!(!node_power_descriptor
.available_power_sources()
.is_set(AvailablePowerSourcesFlag::DisposableBattery));
assert_eq!(
node_power_descriptor.current_power_source(),
CurrentPowerSource::ConstantMainPower
);
assert_eq!(
node_power_descriptor.current_power_source_level(),
CurrentPowerSourceLevel::TwoThirds
);
}
#[test]
fn creating_node_power_descriptor_should_fail() {
let current_power_mode = CurrentPowerMode::Synchronized;
let available_power_sources_flags = bitfield_bits!(
AvailablePowerSourcesFlag;
AvailablePowerSourcesFlag::ConstantMainPower,
);
let available_power_sources = AvailablePowerSources::new(available_power_sources_flags);
let current_power_source = CurrentPowerSource::DisposableBattery;
let current_power_source_level = CurrentPowerSourceLevel::TwoThirds;
let node_power_descriptor = NodePowerDescriptor::new(
current_power_mode,
available_power_sources,
current_power_source,
current_power_source_level,
);
assert!(node_power_descriptor.is_err());
assert_eq!(
node_power_descriptor.unwrap_err(),
Error::CurrentPowerSourceNotAvailable
)
}
}