Compare commits
2 commits
3c466caf07
...
7aa18d0294
Author | SHA1 | Date | |
---|---|---|---|
7aa18d0294 | |||
bafaccd54d |
22 changed files with 2307 additions and 109 deletions
|
@ -1,5 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "powerbox-cli"
|
name = "powerbox-cli"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
@ -7,3 +7,4 @@ description = "Power toolbox for power supply devices"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
powerbox = { version = "0.1", path = "../core" }
|
powerbox = { version = "0.1", path = "../core" }
|
||||||
|
sysfuss = { version = "0.3", path = "../../../sysfs-nav" }
|
||||||
|
|
|
@ -8,4 +8,4 @@ description = "Power toolbox for Linux devices"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sysfuss = { version = "0.3", path = "../../../sysfs-nav" }
|
sysfuss = { version = "0.4", path = "../../../sysfs-nav" }
|
||||||
|
|
|
@ -7,16 +7,7 @@ pub use power_error::PowerError;
|
||||||
mod power_trait;
|
mod power_trait;
|
||||||
pub use power_trait::{Power, PowerOp};
|
pub use power_trait::{Power, PowerOp};
|
||||||
|
|
||||||
mod value;
|
pub mod primitives;
|
||||||
pub use value::Value;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
mod support;
|
||||||
mod tests {
|
pub use support::Support;
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_works() {
|
|
||||||
let result = 2 + 2;
|
|
||||||
assert_eq!(result, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,22 @@
|
||||||
/// All possible errors returned by the [Power] trait.
|
/// All possible errors returned by the [Power] trait.
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum PowerError {
|
pub enum PowerError {
|
||||||
|
/// Input parameter(s) failed validation
|
||||||
|
InvalidInput,
|
||||||
|
/// Input/output error
|
||||||
|
Io(std::io::Error),
|
||||||
|
/// An unknown error occured
|
||||||
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for PowerError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::InvalidInput => write!(f, "invalid input"),
|
||||||
|
Self::Io(e) => write!(f, "io power error: {}", e),
|
||||||
|
Self::Unknown => write!(f, "unknown power error"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for PowerError {}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::PowerError;
|
use super::PowerError;
|
||||||
use super::Value;
|
use super::primitives::Value;
|
||||||
|
|
||||||
/// Power control interface for a device
|
/// Power control interface for a device
|
||||||
pub trait Power<OP: PowerOp, VAL = Value> {
|
pub trait Power<OP: PowerOp, VAL = Value> {
|
||||||
|
@ -17,5 +17,5 @@ pub trait Power<OP: PowerOp, VAL = Value> {
|
||||||
pub trait PowerOp {
|
pub trait PowerOp {
|
||||||
/// Do the operations match?
|
/// Do the operations match?
|
||||||
/// This should ignore values of the operation.
|
/// This should ignore values of the operation.
|
||||||
fn is_eq_op(&self, other: Self) -> bool;
|
fn is_eq_op(&self, other: &Self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
37
crates/core/src/primitives/boolean_number.rs
Normal file
37
crates/core/src/primitives/boolean_number.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/// Boolean value represented as the number 1 or 0
|
||||||
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
|
pub struct BoolNum(pub bool);
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct ParseBoolNumError;
|
||||||
|
|
||||||
|
impl core::fmt::Display for ParseBoolNumError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "provided string was not `1` or `0`")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for ParseBoolNumError {}
|
||||||
|
|
||||||
|
impl core::str::FromStr for BoolNum {
|
||||||
|
type Err = ParseBoolNumError;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s.chars().next() {
|
||||||
|
Some('0') => Ok(Self(false)),
|
||||||
|
Some('1') => Ok(Self(true)),
|
||||||
|
Some(_) => Err(ParseBoolNumError),
|
||||||
|
None => Err(ParseBoolNumError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::fmt::Display for BoolNum {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let c = match self.0 {
|
||||||
|
true => '1',
|
||||||
|
false => '0',
|
||||||
|
};
|
||||||
|
write!(f, "{}", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
13
crates/core/src/primitives/mod.rs
Normal file
13
crates/core/src/primitives/mod.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
//! Primitive types for power control values
|
||||||
|
|
||||||
|
mod boolean_number;
|
||||||
|
pub use boolean_number::BoolNum;
|
||||||
|
|
||||||
|
mod range;
|
||||||
|
pub use range::{Range, RangeList, RangeListParseErr, RangeListItem, RangeListIter};
|
||||||
|
|
||||||
|
mod space_separated_list;
|
||||||
|
pub use space_separated_list::SpacedList;
|
||||||
|
|
||||||
|
mod value;
|
||||||
|
pub use value::Value;
|
207
crates/core/src/primitives/range.rs
Normal file
207
crates/core/src/primitives/range.rs
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
/// A combination of ranges of values and single items.
|
||||||
|
pub struct RangeList<N> {
|
||||||
|
/// Elements of the list range
|
||||||
|
pub items: Vec<RangeListItem<N>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An item of the range list
|
||||||
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
|
pub enum RangeListItem<N> {
|
||||||
|
/// A single item
|
||||||
|
Single(N),
|
||||||
|
/// A range of consecutive items
|
||||||
|
Range(Range<N>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A range of values, usually inclusive.
|
||||||
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
|
pub struct Range<N> {
|
||||||
|
/// Minimum limit of the range
|
||||||
|
pub min: N,
|
||||||
|
/// Maximum limit of the range, usually inclusive
|
||||||
|
pub max: N,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Range list parser error
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum RangeListParseErr<E> {
|
||||||
|
/// The end of the string was encountered before parsing completed
|
||||||
|
UnexpectedEnd,
|
||||||
|
/// An item of the list failed to parse
|
||||||
|
Inner(E),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <E> From<E> for RangeListParseErr<E> {
|
||||||
|
fn from(value: E) -> Self {
|
||||||
|
RangeListParseErr::Inner(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RangeList<usize> {
|
||||||
|
/// Convert range list into iterable and specificy assumption about range inclusiveness
|
||||||
|
/// (true -> ranges will be from min up to and including max; false -> from min up to max)
|
||||||
|
pub fn into_iter(self, inclusive: bool) -> RangeListIter<usize> {
|
||||||
|
RangeListIter {
|
||||||
|
range_list: self,
|
||||||
|
index: 0,
|
||||||
|
inclusive,
|
||||||
|
inner: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterator implementation for RangeList
|
||||||
|
pub struct RangeListIter<N> {
|
||||||
|
range_list: RangeList<N>,
|
||||||
|
index: usize,
|
||||||
|
inclusive: bool,
|
||||||
|
inner: Option<Box<dyn core::iter::Iterator<Item=N>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::iter::IntoIterator for RangeList<usize> {
|
||||||
|
type Item = usize;
|
||||||
|
type IntoIter = RangeListIter<usize>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
RangeListIter {
|
||||||
|
range_list: self,
|
||||||
|
index: 0,
|
||||||
|
inclusive: true,
|
||||||
|
inner: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::iter::Iterator for RangeListIter<usize> {
|
||||||
|
type Item = usize;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if let Some(mut inner) = self.inner.take() {
|
||||||
|
if let Some(next) = inner.next() {
|
||||||
|
self.inner = Some(inner);
|
||||||
|
return Some(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(next_element) = self.range_list.items.get(self.index) {
|
||||||
|
self.index += 1;
|
||||||
|
match next_element {
|
||||||
|
RangeListItem::Single(num) => Some(*num),
|
||||||
|
RangeListItem::Range(range) => {
|
||||||
|
if self.inclusive {
|
||||||
|
self.inner = Some(Box::new(range.min..=range.max))
|
||||||
|
} else {
|
||||||
|
self.inner = Some(Box::new(range.min..range.max))
|
||||||
|
}
|
||||||
|
self.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <E: core::fmt::Display> core::fmt::Display for RangeListParseErr<E> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::UnexpectedEnd => write!(f, "unexpected end"),
|
||||||
|
Self::Inner(e) => write!(f, "inner error: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <E: std::error::Error> std::error::Error for RangeListParseErr<E> {}
|
||||||
|
|
||||||
|
impl <N: core::str::FromStr> core::str::FromStr for RangeList<N> {
|
||||||
|
type Err = RangeListParseErr<<N as core::str::FromStr>::Err>;
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let mut items = Vec::new();
|
||||||
|
if s.is_empty() {
|
||||||
|
return Ok(Self { items });
|
||||||
|
}
|
||||||
|
let mut cur_item_start = 0;
|
||||||
|
let mut range_start = None;
|
||||||
|
for (i, c) in s.chars().enumerate() {
|
||||||
|
match c {
|
||||||
|
'-' => {
|
||||||
|
range_start = Some(N::from_str(&s[cur_item_start..i])?);
|
||||||
|
cur_item_start = i + 1;
|
||||||
|
},
|
||||||
|
',' => {
|
||||||
|
let item = N::from_str(&s[cur_item_start..i])?;
|
||||||
|
if let Some(range_start) = range_start.take() {
|
||||||
|
items.push(RangeListItem::Range(Range { min: range_start, max: item }));
|
||||||
|
} else {
|
||||||
|
items.push(RangeListItem::Single(item));
|
||||||
|
}
|
||||||
|
cur_item_start = i + 1;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cur_item_start >= s.len() {
|
||||||
|
Err(RangeListParseErr::UnexpectedEnd)
|
||||||
|
} else {
|
||||||
|
let item = N::from_str(&s[cur_item_start..s.len()])?;
|
||||||
|
if let Some(range_start) = range_start.take() {
|
||||||
|
items.push(RangeListItem::Range(Range { min: range_start, max: item }));
|
||||||
|
} else {
|
||||||
|
items.push(RangeListItem::Single(item));
|
||||||
|
}
|
||||||
|
Ok(Self { items })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_range_then_single() -> Result<(), RangeListParseErr<<usize as std::str::FromStr>::Err>> {
|
||||||
|
let parsed: RangeList<usize> = "0-15,16".parse()?;
|
||||||
|
assert_eq!(parsed.items, vec![RangeListItem::Range(Range { min: 0, max: 15 }), RangeListItem::Single(16)]);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_single_then_range() -> Result<(), RangeListParseErr<<usize as std::str::FromStr>::Err>> {
|
||||||
|
let parsed: RangeList<usize> = "11,19-22".parse()?;
|
||||||
|
assert_eq!(parsed.items, vec![RangeListItem::Single(11), RangeListItem::Range(Range { min: 19, max: 22 }), ]);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_single() -> Result<(), RangeListParseErr<<usize as std::str::FromStr>::Err>> {
|
||||||
|
let parsed: RangeList<usize> = "42".parse()?;
|
||||||
|
assert_eq!(parsed.items, vec![RangeListItem::Single(42)]);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_nothing() -> Result<(), RangeListParseErr<<usize as std::str::FromStr>::Err>> {
|
||||||
|
let parsed: RangeList<usize> = "".parse()?;
|
||||||
|
assert_eq!(parsed.items, vec![]);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_fail() {
|
||||||
|
let parsed: Result<RangeList<usize>, _> = "1,2-".parse();
|
||||||
|
assert!(parsed.is_err_and(|e| matches!(e, RangeListParseErr::UnexpectedEnd)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn iter_range() {
|
||||||
|
let range_list = RangeList {
|
||||||
|
items: vec![
|
||||||
|
RangeListItem::Single(2),
|
||||||
|
RangeListItem::Range(Range { min: 4, max: 7 }),
|
||||||
|
RangeListItem::Single(3),
|
||||||
|
RangeListItem::Range(Range { min: 20, max: 24 }),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
let actual: Vec<usize> = range_list.into_iter(true).collect();
|
||||||
|
assert_eq!(actual, vec![2, 4, 5, 6, 7, 3, 20, 21, 22, 23, 24])
|
||||||
|
}
|
||||||
|
}
|
35
crates/core/src/primitives/space_separated_list.rs
Normal file
35
crates/core/src/primitives/space_separated_list.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
/// A list of items separated by a list
|
||||||
|
pub struct SpacedList<T>(pub Vec<T>);
|
||||||
|
|
||||||
|
impl <T: FromStr> FromStr for SpacedList<T> {
|
||||||
|
type Err = <T as FromStr>::Err;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
let mut results = Vec::new();
|
||||||
|
for chars in s.split(' ') {
|
||||||
|
if !chars.is_empty() {
|
||||||
|
results.push(T::from_str(chars)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Self(results))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_list_of_str() {
|
||||||
|
let strings: SpacedList<String> = "abcd wxyz".parse().expect("fail");
|
||||||
|
assert_eq!(strings.0, vec!["abcd", "wxyz"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_list_of_num() {
|
||||||
|
let strings: SpacedList<usize> = "1 2 3 4 5 6 7 8 9 10 11".parse().expect("fail");
|
||||||
|
assert_eq!(strings.0, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
|
||||||
|
}
|
||||||
|
}
|
162
crates/core/src/primitives/value.rs
Normal file
162
crates/core/src/primitives/value.rs
Normal file
|
@ -0,0 +1,162 @@
|
||||||
|
/// Value of a power reading
|
||||||
|
pub enum Value<C = Box<dyn std::any::Any>> {
|
||||||
|
/// floating point value
|
||||||
|
Float(f64),
|
||||||
|
/// unsigned integer value
|
||||||
|
UInt(u64),
|
||||||
|
/// signed integer value
|
||||||
|
Int(i64),
|
||||||
|
/// boolean value
|
||||||
|
Bool(bool),
|
||||||
|
/// string value
|
||||||
|
String(String),
|
||||||
|
/// custom value
|
||||||
|
Custom(C),
|
||||||
|
/// unknown value
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Value {
|
||||||
|
/// Convert to f64, if numeric type
|
||||||
|
pub fn as_f64(&self) -> Option<f64> {
|
||||||
|
match self {
|
||||||
|
Self::Float(f) => Some(*f),
|
||||||
|
Self::UInt(u) => Some(*u as _),
|
||||||
|
Self::Int(i) => Some(*i as _),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert to u64, if numeric type
|
||||||
|
pub fn as_u64(&self) -> Option<u64> {
|
||||||
|
match self {
|
||||||
|
Self::Float(f) => Some(*f as _),
|
||||||
|
Self::UInt(u) => Some(*u),
|
||||||
|
Self::Int(i) => Some(*i as _),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert to u64, if numeric type
|
||||||
|
pub fn as_i64(&self) -> Option<i64> {
|
||||||
|
match self {
|
||||||
|
Self::Float(f) => Some(*f as _),
|
||||||
|
Self::UInt(u) => Some(*u as _),
|
||||||
|
Self::Int(i) => Some(*i),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert to string, if possible
|
||||||
|
pub fn as_str(&self) -> Option<String> {
|
||||||
|
match self {
|
||||||
|
Self::Float(f) => Some(f.to_string()),
|
||||||
|
Self::UInt(u) => Some(u.to_string()),
|
||||||
|
Self::Int(i) => Some(i.to_string()),
|
||||||
|
Self::Bool(b) => Some(b.to_string()),
|
||||||
|
Self::String(s) => Some(s.to_owned()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is value some sort of number?
|
||||||
|
pub fn is_numeric(&self) -> bool {
|
||||||
|
matches!(self, Self::Float(_) | Self::Int(_) | Self::UInt(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is value unknown?
|
||||||
|
pub fn is_unknown(&self) -> bool {
|
||||||
|
matches!(self, Self::Unknown)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is value a boolean?
|
||||||
|
pub fn is_bool(&self) -> bool {
|
||||||
|
matches!(self, Self::Bool(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is value a string?
|
||||||
|
pub fn is_str(&self) -> bool {
|
||||||
|
matches!(self, Self::String(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Is value a custom type?
|
||||||
|
pub fn is_custom(&self) -> bool {
|
||||||
|
matches!(self, Self::Custom(_))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C> std::convert::From<f64> for Value<C> {
|
||||||
|
fn from(value: f64) -> Self {
|
||||||
|
Self::Float(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C> std::convert::From<u64> for Value<C> {
|
||||||
|
fn from(value: u64) -> Self {
|
||||||
|
Self::UInt(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C> std::convert::From<i64> for Value<C> {
|
||||||
|
fn from(value: i64) -> Self {
|
||||||
|
Self::Int(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C> std::convert::From<bool> for Value<C> {
|
||||||
|
fn from(value: bool) -> Self {
|
||||||
|
Self::Bool(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C> std::convert::From<String> for Value<C> {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Self::String(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C> std::convert::From<&str> for Value<C> {
|
||||||
|
fn from(value: &str) -> Self {
|
||||||
|
Self::String(value.to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B> Value<B> {
|
||||||
|
|
||||||
|
/// Convert standard value types between generic variants.
|
||||||
|
/// Fails if trying to convert between custom types
|
||||||
|
pub fn maybe_convert<A>(value: Value<A>) -> Option<Self> {
|
||||||
|
match value {
|
||||||
|
Value::<A>::Float(f) => Some(Value::<B>::Float(f)),
|
||||||
|
Value::<A>::UInt(u) => Some(Value::<B>::UInt(u)),
|
||||||
|
Value::<A>::Int(i) => Some(Value::<B>::Int(i)),
|
||||||
|
Value::<A>::Bool(b) => Some(Value::<B>::Bool(b)),
|
||||||
|
Value::<A>::String(s) => Some(Value::<B>::String(s)),
|
||||||
|
Value::<A>::Custom(_) => None,
|
||||||
|
Value::<A>::Unknown => Some(Value::Unknown),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Value {
|
||||||
|
/// Convert a specialised Value<A: Any> into a Value<Box<Any>>.
|
||||||
|
pub fn into_any<A: std::any::Any>(value: Value<A>) -> Self {
|
||||||
|
match value {
|
||||||
|
Value::<A>::Float(f) => Value::Float(f),
|
||||||
|
Value::<A>::UInt(u) => Value::UInt(u),
|
||||||
|
Value::<A>::Int(i) => Value::Int(i),
|
||||||
|
Value::<A>::Bool(b) => Value::Bool(b),
|
||||||
|
Value::<A>::String(s) => Value::String(s),
|
||||||
|
Value::<A>::Custom(c) => Value::Custom(Box::new(c)),
|
||||||
|
Value::<A>::Unknown => Value::Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If only this could not include the above types in C...
|
||||||
|
impl<C> std::convert::From<C> for Value<C> {
|
||||||
|
fn from(value: C) -> Self {
|
||||||
|
Self::Custom(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
|
@ -1,7 +1,11 @@
|
||||||
/// Support status of a power interface
|
/// Support status of a power interface
|
||||||
pub enum Support {
|
pub enum Support {
|
||||||
|
/// Only read operations are supported
|
||||||
Get,
|
Get,
|
||||||
|
/// Only write operations are supported
|
||||||
Set,
|
Set,
|
||||||
|
/// Read and write are supported
|
||||||
GetSet,
|
GetSet,
|
||||||
|
/// Nothing is supported
|
||||||
Unsupported,
|
Unsupported,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
/// Value of a power reading
|
|
||||||
pub enum Value<C = Box<dyn std::any::Any>> {
|
|
||||||
/// floating point value
|
|
||||||
Float(f64),
|
|
||||||
/// unsigned integer value
|
|
||||||
UInt(u64),
|
|
||||||
/// signed integer value
|
|
||||||
Int(i64),
|
|
||||||
/// boolean value
|
|
||||||
Bool(bool),
|
|
||||||
/// custom value
|
|
||||||
Custom(C),
|
|
||||||
/// unknown value
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Value {
|
|
||||||
/// Convert to f64, if numeric type
|
|
||||||
pub fn as_f64(&self) -> Option<f64> {
|
|
||||||
match self {
|
|
||||||
Self::Float(f) => Some(*f),
|
|
||||||
Self::UInt(u) => Some(*u as _),
|
|
||||||
Self::Int(i) => Some(*i as _),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert to u64, if numeric type
|
|
||||||
pub fn as_u64(&self) -> Option<u64> {
|
|
||||||
match self {
|
|
||||||
Self::Float(f) => Some(*f as _),
|
|
||||||
Self::UInt(u) => Some(*u),
|
|
||||||
Self::Int(i) => Some(*i as _),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert to u64, if numeric type
|
|
||||||
pub fn as_i64(&self) -> Option<i64> {
|
|
||||||
match self {
|
|
||||||
Self::Float(f) => Some(*f as _),
|
|
||||||
Self::UInt(u) => Some(*u as _),
|
|
||||||
Self::Int(i) => Some(*i),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Is value some sort of number?
|
|
||||||
pub fn is_numeric(&self) -> bool {
|
|
||||||
matches!(self, Self::Float(_) | Self::Int(_) | Self::UInt(_))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Is value unknown?
|
|
||||||
pub fn is_unknown(&self) -> bool {
|
|
||||||
matches!(self, Self::Unknown)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Is value a boolean?
|
|
||||||
pub fn is_bool(&self) -> bool {
|
|
||||||
matches!(self, Self::Bool(_))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Is value a custom type?
|
|
||||||
pub fn is_custom(&self) -> bool {
|
|
||||||
matches!(self, Self::Custom(_))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,3 +7,4 @@ description = "Power toolbox for processors"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
powerbox = { version = "0.1", path = "../core" }
|
powerbox = { version = "0.1", path = "../core" }
|
||||||
|
sysfuss = { version = "0.4", path = "../../../sysfs-nav" }
|
||||||
|
|
616
crates/procbox/src/cpu/basic_general.rs
Normal file
616
crates/procbox/src/cpu/basic_general.rs
Normal file
|
@ -0,0 +1,616 @@
|
||||||
|
use powerbox::{Power, PowerOp};
|
||||||
|
use powerbox::primitives::RangeList;
|
||||||
|
use sysfuss::{BasicEntityPath, SysEntityAttributesExt};
|
||||||
|
|
||||||
|
use super::{CpuPower, CpuPowerOp};
|
||||||
|
|
||||||
|
pub(super) const DEFAULT_CPU_ROOT: &str = "/sys/devices/system/cpu";
|
||||||
|
const CPUS_PRESENT: &str = "present";
|
||||||
|
const CPUS_POSSIBLE: &str = "possible";
|
||||||
|
const CPUS_KERNEL_MAX: &str = "kernel_max";
|
||||||
|
const CPUS_ONLINE: &str = "online";
|
||||||
|
const CPUS_OFFLINE: &str = "offline";
|
||||||
|
const CPUS_SMT_ACTIVE: &str = "smt/active";
|
||||||
|
const CPUS_SMT_CONTROL: &str = "smt/control";
|
||||||
|
|
||||||
|
/// General CPUs subsystem functionality
|
||||||
|
pub struct BasicCpus {
|
||||||
|
sysfs: BasicEntityPath,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BasicCpus {
|
||||||
|
fn is_online(&self) -> bool {
|
||||||
|
self.sysfs.attribute::<String>(CPUS_PRESENT).is_ok_and(|s| s.trim_end() != "")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_exists(&self) -> bool {
|
||||||
|
self.sysfs.as_ref().exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
sysfs: BasicEntityPath::new(DEFAULT_CPU_ROOT),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_root(p: impl AsRef<std::path::Path>) -> Self {
|
||||||
|
Self {
|
||||||
|
sysfs: BasicEntityPath::new(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum BasicCpusOp {
|
||||||
|
SMT(SMT),
|
||||||
|
Present(GetPresent),
|
||||||
|
Possible(GetPossible),
|
||||||
|
KernelMax(GetKernelMax),
|
||||||
|
Online(GetCpusOnline),
|
||||||
|
Offline(GetCpusOffline),
|
||||||
|
//CpuFreq(),
|
||||||
|
//CpuIdle(),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PowerOp for BasicCpusOp {
|
||||||
|
fn is_eq_op(&self, other: &Self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::SMT(smt) => if let Self::SMT(other) = other { smt.is_eq_op(other) } else { false },
|
||||||
|
Self::Present(_) => matches!(other, Self::Present(_)),
|
||||||
|
Self::Possible(_) => matches!(other, Self::Possible(_)),
|
||||||
|
Self::KernelMax(_) => matches!(other, Self::KernelMax(_)),
|
||||||
|
Self::Online(_) => matches!(other, Self::Online(_)),
|
||||||
|
Self::Offline(_) => matches!(other, Self::Offline(_)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuPowerOp for BasicCpusOp {}
|
||||||
|
|
||||||
|
impl Power<BasicCpusOp, powerbox::primitives::Value<RangeList<usize>>> for BasicCpus {
|
||||||
|
fn is_on(&self) -> bool {
|
||||||
|
self.is_online()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_available(&self) -> bool {
|
||||||
|
self.is_exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_operations(&self) -> Box<dyn core::iter::Iterator<Item=BasicCpusOp>> {
|
||||||
|
Box::new(
|
||||||
|
Power::<SMT, _>::supported_operations(self).map(core::convert::Into::<BasicCpusOp>::into)
|
||||||
|
.chain(Power::<GetPresent, _>::supported_operations(self).map(core::convert::Into::<BasicCpusOp>::into))
|
||||||
|
.chain(Power::<GetPossible, _>::supported_operations(self).map(core::convert::Into::<BasicCpusOp>::into))
|
||||||
|
.chain(Power::<GetKernelMax, _>::supported_operations(self).map(core::convert::Into::<BasicCpusOp>::into))
|
||||||
|
.chain(Power::<GetCpusOnline, _>::supported_operations(self).map(core::convert::Into::<BasicCpusOp>::into))
|
||||||
|
.chain(Power::<GetCpusOffline, _>::supported_operations(self).map(core::convert::Into::<BasicCpusOp>::into))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn act(&self, op: BasicCpusOp) -> Result<powerbox::primitives::Value<RangeList<usize>>, powerbox::PowerError> {
|
||||||
|
match op {
|
||||||
|
BasicCpusOp::SMT(smt) => Power::act(self, smt).map(|x| powerbox::primitives::Value::maybe_convert(x).unwrap()),
|
||||||
|
BasicCpusOp::Present(present) => Power::act(self, present).map(|x| powerbox::primitives::Value::Custom(x)),
|
||||||
|
BasicCpusOp::Possible(possible) => Power::act(self, possible).map(|x| powerbox::primitives::Value::Custom(x)),
|
||||||
|
BasicCpusOp::KernelMax(kmax) => Power::act(self, kmax).map(|x| powerbox::primitives::Value::UInt(x as _)),
|
||||||
|
BasicCpusOp::Online(onlines) => Power::act(self, onlines).map(|x| powerbox::primitives::Value::Custom(x)),
|
||||||
|
BasicCpusOp::Offline(offlines) => Power::act(self, offlines).map(|x| powerbox::primitives::Value::Custom(x)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Power<BasicCpusOp> for BasicCpus {
|
||||||
|
fn is_on(&self) -> bool {
|
||||||
|
self.is_online()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_available(&self) -> bool {
|
||||||
|
self.is_exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_operations(&self) -> Box<dyn core::iter::Iterator<Item=BasicCpusOp>> {
|
||||||
|
Power::<BasicCpusOp, powerbox::primitives::Value<RangeList<usize>>>::supported_operations(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn act(&self, op: BasicCpusOp) -> Result<powerbox::primitives::Value, powerbox::PowerError> {
|
||||||
|
Power::<BasicCpusOp, powerbox::primitives::Value<RangeList<usize>>>::act(self, op)
|
||||||
|
.map(powerbox::primitives::Value::into_any)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuPower<BasicCpusOp> for BasicCpus {}
|
||||||
|
|
||||||
|
pub struct SetSMT(pub bool);
|
||||||
|
pub struct GetSMT;
|
||||||
|
pub enum SMT {
|
||||||
|
Get(GetSMT),
|
||||||
|
Set(SetSMT),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PowerOp for SetSMT {
|
||||||
|
fn is_eq_op(&self, _: &Self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuPowerOp for SetSMT {}
|
||||||
|
|
||||||
|
impl Power<SetSMT, ()> for BasicCpus {
|
||||||
|
fn is_on(&self) -> bool {
|
||||||
|
self.is_online()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_available(&self) -> bool {
|
||||||
|
self.is_exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_operations(&self) -> Box<dyn core::iter::Iterator<Item=SetSMT>> {
|
||||||
|
if self.sysfs.exists(&CPUS_SMT_CONTROL) { Box::new(core::iter::once(SetSMT(true))) } else { Box::new(core::iter::empty()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn act(&self, op: SetSMT) -> Result<(), powerbox::PowerError> {
|
||||||
|
if self.sysfs.exists(&CPUS_SMT_CONTROL) {
|
||||||
|
self.sysfs.set(CPUS_SMT_CONTROL, if op.0 { "on" } else { "off" }).map_err(powerbox::PowerError::Io)
|
||||||
|
} else {
|
||||||
|
Err(powerbox::PowerError::Unknown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::convert::Into<SMT> for SetSMT {
|
||||||
|
fn into(self) -> SMT {
|
||||||
|
SMT::Set(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_cpus_set_smt {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cpus_set_smt_fail() {
|
||||||
|
let target = BasicCpus::new();
|
||||||
|
assert!(Power::<SetSMT, _>::supported_operations(&target).count() == 1);
|
||||||
|
let result = target.act(SetSMT(true));
|
||||||
|
assert!(result.is_err());
|
||||||
|
let err = result.unwrap_err();
|
||||||
|
if let powerbox::PowerError::Io(io_err) = err {
|
||||||
|
assert_eq!(io_err.kind(), std::io::ErrorKind::PermissionDenied);
|
||||||
|
} else {
|
||||||
|
panic!("Expected IO error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PowerOp for GetSMT {
|
||||||
|
fn is_eq_op(&self, _: &Self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuPowerOp for GetSMT {}
|
||||||
|
|
||||||
|
impl Power<GetSMT, bool> for BasicCpus {
|
||||||
|
fn is_on(&self) -> bool {
|
||||||
|
self.is_online()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_available(&self) -> bool {
|
||||||
|
self.is_exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_operations(&self) -> Box<dyn core::iter::Iterator<Item=GetSMT>> {
|
||||||
|
if self.sysfs.exists(&CPUS_SMT_ACTIVE) { Box::new(core::iter::once(GetSMT)) } else { Box::new(core::iter::empty()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn act(&self, _: GetSMT) -> Result<bool, powerbox::PowerError> {
|
||||||
|
if self.sysfs.exists(&CPUS_SMT_ACTIVE) {
|
||||||
|
match self.sysfs.attribute::<String>(CPUS_SMT_ACTIVE) {
|
||||||
|
Ok(s) => Ok(s.trim_end() == "1"),
|
||||||
|
Err(sysfuss::EitherErr2::First(e)) => Err(powerbox::PowerError::Io(e)),
|
||||||
|
Err(sysfuss::EitherErr2::Second(_)) => panic!("Infallible error"),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(powerbox::PowerError::Unknown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::convert::Into<SMT> for GetSMT {
|
||||||
|
fn into(self) -> SMT {
|
||||||
|
SMT::Get(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_cpus_get_smt {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cpus_get_smt() -> Result<(), powerbox::PowerError> {
|
||||||
|
let target = BasicCpus::new();
|
||||||
|
assert!(Power::<GetSMT, _>::supported_operations(&target).count() == 1);
|
||||||
|
let smt = target.act(GetSMT)?;
|
||||||
|
assert_eq!(smt, true);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PowerOp for SMT {
|
||||||
|
fn is_eq_op(&self, other: &Self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Get(_) => matches!(other, Self::Get(_)),
|
||||||
|
Self::Set(_) => matches!(other, Self::Set(_)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuPowerOp for SMT {}
|
||||||
|
|
||||||
|
impl Power<SMT> for BasicCpus {
|
||||||
|
fn is_on(&self) -> bool {
|
||||||
|
self.is_online()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_available(&self) -> bool {
|
||||||
|
self.is_exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_operations(&self) -> Box<dyn core::iter::Iterator<Item=SMT>> {
|
||||||
|
Box::new(Power::<GetSMT, _>::supported_operations(self).map(core::convert::Into::<SMT>::into)
|
||||||
|
.chain(Power::<SetSMT, _>::supported_operations(self).map(core::convert::Into::<SMT>::into)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn act(&self, op: SMT) -> Result<powerbox::primitives::Value, powerbox::PowerError> {
|
||||||
|
match op {
|
||||||
|
SMT::Get(get) => Power::<GetSMT, _>::act(self, get).map(core::convert::Into::into),
|
||||||
|
SMT::Set(set) => Power::<SetSMT, _>::act(self, set).map(|_| powerbox::primitives::Value::Unknown),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::convert::Into<BasicCpusOp> for SMT {
|
||||||
|
fn into(self) -> BasicCpusOp {
|
||||||
|
BasicCpusOp::SMT(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GetPresent; // Option<powerbox::primitives::RangeList<usize>
|
||||||
|
impl PowerOp for GetPresent {
|
||||||
|
fn is_eq_op(&self, _: &Self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuPowerOp for GetPresent {}
|
||||||
|
|
||||||
|
impl Power<GetPresent, RangeList<usize>> for BasicCpus {
|
||||||
|
fn is_on(&self) -> bool {
|
||||||
|
self.is_online()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_available(&self) -> bool {
|
||||||
|
self.is_exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_operations(&self) -> Box<dyn core::iter::Iterator<Item=GetPresent>> {
|
||||||
|
if self.sysfs.exists(&CPUS_PRESENT) { Box::new(core::iter::once(GetPresent)) } else { Box::new(core::iter::empty()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn act(&self, _: GetPresent) -> Result<RangeList<usize>, powerbox::PowerError> {
|
||||||
|
if self.sysfs.exists(&CPUS_PRESENT) {
|
||||||
|
self.sysfs.attribute::<RangeList<usize>>(CPUS_PRESENT).map_err(|e| match e {
|
||||||
|
sysfuss::EitherErr2::First(e) => powerbox::PowerError::Io(e),
|
||||||
|
sysfuss::EitherErr2::Second(_) => powerbox::PowerError::Unknown,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(powerbox::PowerError::Unknown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::convert::Into<BasicCpusOp> for GetPresent {
|
||||||
|
fn into(self) -> BasicCpusOp {
|
||||||
|
BasicCpusOp::Present(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_cpus_present {
|
||||||
|
use super::*;
|
||||||
|
use powerbox::primitives::{RangeListItem, Range};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cpus_present() -> Result<(), powerbox::PowerError> {
|
||||||
|
let target = BasicCpus::new();
|
||||||
|
assert!(Power::<GetPresent, _>::supported_operations(&target).count() == 1);
|
||||||
|
let rl = target.act(GetPresent)?;
|
||||||
|
assert_eq!(rl.items, vec![RangeListItem::Range(Range { min: 0, max: 15 })]);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GetPossible; // Option<powerbox::primitives::RangeList<usize>
|
||||||
|
impl PowerOp for GetPossible {
|
||||||
|
fn is_eq_op(&self, _: &Self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuPowerOp for GetPossible {}
|
||||||
|
|
||||||
|
impl Power<GetPossible, RangeList<usize>> for BasicCpus {
|
||||||
|
fn is_on(&self) -> bool {
|
||||||
|
self.is_online()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_available(&self) -> bool {
|
||||||
|
self.is_exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_operations(&self) -> Box<dyn core::iter::Iterator<Item=GetPossible>> {
|
||||||
|
if self.sysfs.exists(&CPUS_POSSIBLE) { Box::new(core::iter::once(GetPossible)) } else { Box::new(core::iter::empty()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn act(&self, _: GetPossible) -> Result<RangeList<usize>, powerbox::PowerError> {
|
||||||
|
if self.sysfs.exists(&CPUS_POSSIBLE) {
|
||||||
|
self.sysfs.attribute::<RangeList<usize>>(CPUS_POSSIBLE).map_err(|e| match e {
|
||||||
|
sysfuss::EitherErr2::First(e) => powerbox::PowerError::Io(e),
|
||||||
|
sysfuss::EitherErr2::Second(_) => powerbox::PowerError::Unknown,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(powerbox::PowerError::Unknown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_cpus_possible {
|
||||||
|
use super::*;
|
||||||
|
use powerbox::primitives::{RangeListItem, Range};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cpus_possible() -> Result<(), powerbox::PowerError> {
|
||||||
|
let target = BasicCpus::new();
|
||||||
|
assert!(Power::<GetPossible, _>::supported_operations(&target).count() == 1);
|
||||||
|
let rl = target.act(GetPossible)?;
|
||||||
|
assert_eq!(rl.items, vec![RangeListItem::Range(Range { min: 0, max: 15 })]);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::convert::Into<BasicCpusOp> for GetPossible {
|
||||||
|
fn into(self) -> BasicCpusOp {
|
||||||
|
BasicCpusOp::Possible(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GetKernelMax; // usize
|
||||||
|
impl PowerOp for GetKernelMax {
|
||||||
|
fn is_eq_op(&self, _: &Self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuPowerOp for GetKernelMax {}
|
||||||
|
|
||||||
|
impl Power<GetKernelMax, usize> for BasicCpus {
|
||||||
|
fn is_on(&self) -> bool {
|
||||||
|
self.is_online()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_available(&self) -> bool {
|
||||||
|
self.is_exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_operations(&self) -> Box<dyn core::iter::Iterator<Item=GetKernelMax>> {
|
||||||
|
if self.sysfs.exists(&CPUS_KERNEL_MAX) { Box::new(core::iter::once(GetKernelMax)) } else { Box::new(core::iter::empty()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn act(&self, _: GetKernelMax) -> Result<usize, powerbox::PowerError> {
|
||||||
|
if self.sysfs.exists(&CPUS_KERNEL_MAX) {
|
||||||
|
self.sysfs.attribute::<usize>(CPUS_KERNEL_MAX).map_err(|e| match e {
|
||||||
|
sysfuss::EitherErr2::First(e) => powerbox::PowerError::Io(e),
|
||||||
|
sysfuss::EitherErr2::Second(_) => powerbox::PowerError::Unknown,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(powerbox::PowerError::Unknown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::convert::Into<BasicCpusOp> for GetKernelMax {
|
||||||
|
fn into(self) -> BasicCpusOp {
|
||||||
|
BasicCpusOp::KernelMax(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_cpus_kmax {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cpus_kmax() -> Result<(), powerbox::PowerError> {
|
||||||
|
let target = BasicCpus::new();
|
||||||
|
assert!(Power::<GetKernelMax, _>::supported_operations(&target).count() == 1);
|
||||||
|
let kmax = target.act(GetKernelMax)?;
|
||||||
|
assert_eq!(kmax, 8191);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GetCpusOnline; // Option<powerbox::primitives::RangeList<usize>
|
||||||
|
impl PowerOp for GetCpusOnline {
|
||||||
|
fn is_eq_op(&self, _: &Self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuPowerOp for GetCpusOnline {}
|
||||||
|
|
||||||
|
impl Power<GetCpusOnline, RangeList<usize>> for BasicCpus {
|
||||||
|
fn is_on(&self) -> bool {
|
||||||
|
self.is_online()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_available(&self) -> bool {
|
||||||
|
self.is_exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_operations(&self) -> Box<dyn core::iter::Iterator<Item=GetCpusOnline>> {
|
||||||
|
if self.sysfs.exists(&CPUS_ONLINE) { Box::new(core::iter::once(GetCpusOnline)) } else { Box::new(core::iter::empty()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn act(&self, _: GetCpusOnline) -> Result<RangeList<usize>, powerbox::PowerError> {
|
||||||
|
if self.sysfs.exists(&CPUS_ONLINE) {
|
||||||
|
self.sysfs.attribute::<RangeList<usize>>(CPUS_ONLINE).map_err(|e| match e {
|
||||||
|
sysfuss::EitherErr2::First(e) => powerbox::PowerError::Io(e),
|
||||||
|
sysfuss::EitherErr2::Second(_) => powerbox::PowerError::Unknown,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(powerbox::PowerError::Unknown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::convert::Into<BasicCpusOp> for GetCpusOnline {
|
||||||
|
fn into(self) -> BasicCpusOp {
|
||||||
|
BasicCpusOp::Online(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_cpus_online {
|
||||||
|
use super::*;
|
||||||
|
use powerbox::primitives::{RangeListItem, Range};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cpus_online() -> Result<(), powerbox::PowerError> {
|
||||||
|
let target = BasicCpus::new();
|
||||||
|
assert!(Power::<GetCpusOnline, _>::supported_operations(&target).count() == 1);
|
||||||
|
let rl = target.act(GetCpusOnline)?;
|
||||||
|
assert_eq!(rl.items, vec![RangeListItem::Range(Range { min: 0, max: 15 })]);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GetCpusOffline; // Option<powerbox::primitives::RangeList<usize>
|
||||||
|
impl PowerOp for GetCpusOffline {
|
||||||
|
fn is_eq_op(&self, _: &Self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuPowerOp for GetCpusOffline {}
|
||||||
|
|
||||||
|
impl Power<GetCpusOffline, RangeList<usize>> for BasicCpus {
|
||||||
|
fn is_on(&self) -> bool {
|
||||||
|
self.is_online()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_available(&self) -> bool {
|
||||||
|
self.is_exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_operations(&self) -> Box<dyn core::iter::Iterator<Item=GetCpusOffline>> {
|
||||||
|
if self.sysfs.exists(&CPUS_OFFLINE) { Box::new(core::iter::once(GetCpusOffline)) } else { Box::new(core::iter::empty()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn act(&self, _: GetCpusOffline) -> Result<RangeList<usize>, powerbox::PowerError> {
|
||||||
|
if self.sysfs.exists(&CPUS_OFFLINE) {
|
||||||
|
self.sysfs.attribute::<RangeList<usize>>(CPUS_OFFLINE).map_err(|e| match e {
|
||||||
|
sysfuss::EitherErr2::First(e) => powerbox::PowerError::Io(e),
|
||||||
|
sysfuss::EitherErr2::Second(_) => powerbox::PowerError::Unknown,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(powerbox::PowerError::Unknown)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::convert::Into<BasicCpusOp> for GetCpusOffline {
|
||||||
|
fn into(self) -> BasicCpusOp {
|
||||||
|
BasicCpusOp::Offline(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_cpus_offline {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cpus_offline() -> Result<(), powerbox::PowerError> {
|
||||||
|
let target = BasicCpus::new();
|
||||||
|
assert!(Power::<GetCpusOffline, _>::supported_operations(&target).count() == 1);
|
||||||
|
let rl = target.act(GetCpusOffline)?;
|
||||||
|
assert_eq!(rl.items, vec![]);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get CPU by index
|
||||||
|
pub struct GetCpu(usize);
|
||||||
|
impl PowerOp for GetCpu {
|
||||||
|
fn is_eq_op(&self, _: &Self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuPowerOp for GetCpu {}
|
||||||
|
|
||||||
|
impl Power<GetCpu, super::BasicCpu> for BasicCpus {
|
||||||
|
fn is_on(&self) -> bool {
|
||||||
|
self.is_online()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_available(&self) -> bool {
|
||||||
|
self.is_exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_operations(&self) -> Box<dyn core::iter::Iterator<Item=GetCpu>> {
|
||||||
|
if let Ok(possible_cpus) = self.act(GetPossible) {
|
||||||
|
Box::new(possible_cpus.into_iter(true)
|
||||||
|
.map(|index| GetCpu(index)))
|
||||||
|
} else {
|
||||||
|
Box::new(core::iter::empty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn act(&self, i: GetCpu) -> Result<super::BasicCpu, powerbox::PowerError> {
|
||||||
|
Ok(super::BasicCpu::with_root(self.sysfs.as_ref().join(format!("cpu{}", i.0))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get all CPUs present in the system
|
||||||
|
pub struct GetCpus;
|
||||||
|
impl PowerOp for GetCpus {
|
||||||
|
fn is_eq_op(&self, _: &Self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CpuPowerOp for GetCpus {}
|
||||||
|
|
||||||
|
impl Power<GetCpus, Vec<super::BasicCpu>> for BasicCpus {
|
||||||
|
fn is_on(&self) -> bool {
|
||||||
|
self.is_online()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_available(&self) -> bool {
|
||||||
|
self.is_exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_operations(&self) -> Box<dyn core::iter::Iterator<Item=GetCpus>> {
|
||||||
|
Box::new(core::iter::once(GetCpus))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn act(&self, _: GetCpus) -> Result<Vec<super::BasicCpu>, powerbox::PowerError> {
|
||||||
|
let possible_cpus: Vec<_> = Power::<GetCpu, _>::supported_operations(self).collect();
|
||||||
|
let mut results = Vec::with_capacity(possible_cpus.len());
|
||||||
|
for cpu in possible_cpus {
|
||||||
|
results.push(Power::<GetCpu, _>::act(self, cpu)?);
|
||||||
|
}
|
||||||
|
Ok(results)
|
||||||
|
}
|
||||||
|
}
|
1190
crates/procbox/src/cpu/basic_single.rs
Normal file
1190
crates/procbox/src/cpu/basic_single.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,5 @@
|
||||||
/// Power control interface for a CPU
|
/// Power control interface for a CPU
|
||||||
pub trait CpuPower<OP: CpuPowerOp, VAL = powerbox::Value>: powerbox::Power<OP, VAL> {}
|
pub trait CpuPower<OP: CpuPowerOp, VAL = powerbox::primitives::Value>: powerbox::Power<OP, VAL> {}
|
||||||
|
|
||||||
/// Power control operation(s) of a CPU
|
/// Power control operation(s) of a CPU
|
||||||
pub trait CpuPowerOp: powerbox::PowerOp {}
|
pub trait CpuPowerOp: powerbox::PowerOp {}
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
//! CPU power management interface
|
//! CPU power management interface
|
||||||
mod cpu_trait;
|
mod cpu_trait;
|
||||||
pub use cpu_trait::{CpuPower, CpuPowerOp};
|
pub use cpu_trait::{CpuPower, CpuPowerOp};
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub mod basic_general;
|
||||||
|
pub use basic_general::BasicCpus;
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub mod basic_single;
|
||||||
|
pub use basic_single::BasicCpu;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/// Power control interface for a GPU
|
/// Power control interface for a GPU
|
||||||
pub trait GpuPower<OP: GpuPowerOp, VAL = powerbox::Value>: powerbox::Power<OP, VAL> {}
|
pub trait GpuPower<OP: GpuPowerOp, VAL = powerbox::primitives::Value>: powerbox::Power<OP, VAL> {}
|
||||||
|
|
||||||
/// Power control operation(s) of a GPU
|
/// Power control operation(s) of a GPU
|
||||||
pub trait GpuPowerOp: powerbox::PowerOp {}
|
pub trait GpuPowerOp: powerbox::PowerOp {}
|
||||||
|
|
|
@ -4,13 +4,4 @@
|
||||||
pub mod cpu;
|
pub mod cpu;
|
||||||
pub mod gpu;
|
pub mod gpu;
|
||||||
|
|
||||||
#[cfg(test)]
|
pub mod combo;
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_works() {
|
|
||||||
let result = 2 + 2;
|
|
||||||
assert_eq!(result, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
14
src/lib.rs
14
src/lib.rs
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
3
src/main.rs
Normal file
3
src/main.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
fn main() {
|
||||||
|
println!("Hello powerbox-cli");
|
||||||
|
}
|
Loading…
Reference in a new issue