From bafaccd54df72e372039b92e216dae10e25f109a Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Fri, 14 Jun 2024 21:08:13 -0400 Subject: [PATCH] Add basic components, CPUs interface (not single CPU yet) --- Cargo.toml | 2 + crates/batbox/Cargo.toml | 1 + crates/core/src/lib.rs | 15 +- crates/core/src/power_error.rs | 20 +- crates/core/src/power_trait.rs | 4 +- crates/core/src/primitives/mod.rs | 7 + crates/core/src/primitives/range.rs | 129 ++++++ crates/core/src/primitives/value.rs | 162 +++++++ crates/core/src/support.rs | 4 + crates/core/src/value.rs | 67 --- crates/procbox/Cargo.toml | 1 + crates/procbox/src/cpu/basic.rs | 633 ++++++++++++++++++++++++++++ crates/procbox/src/cpu/cpu_trait.rs | 2 +- crates/procbox/src/cpu/mod.rs | 4 + crates/procbox/src/gpu/gpu_trait.rs | 2 +- crates/procbox/src/lib.rs | 11 +- src/lib.rs | 14 - src/main.rs | 3 + 18 files changed, 973 insertions(+), 108 deletions(-) create mode 100644 crates/core/src/primitives/mod.rs create mode 100644 crates/core/src/primitives/range.rs create mode 100644 crates/core/src/primitives/value.rs delete mode 100644 crates/core/src/value.rs create mode 100644 crates/procbox/src/cpu/basic.rs delete mode 100644 src/lib.rs create mode 100644 src/main.rs diff --git a/Cargo.toml b/Cargo.toml index d76d068..1a80f68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,7 @@ [package] name = "powerbox-cli" +version = "0.1.0" +edition = "2021" [workspace.package] version = "0.1.0" diff --git a/crates/batbox/Cargo.toml b/crates/batbox/Cargo.toml index b18b02e..c92c503 100644 --- a/crates/batbox/Cargo.toml +++ b/crates/batbox/Cargo.toml @@ -7,3 +7,4 @@ description = "Power toolbox for power supply devices" [dependencies] powerbox = { version = "0.1", path = "../core" } +sysfuss = { version = "0.3", path = "../../../sysfs-nav" } diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index c509521..0302120 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -7,16 +7,7 @@ pub use power_error::PowerError; mod power_trait; pub use power_trait::{Power, PowerOp}; -mod value; -pub use value::Value; +pub mod primitives; -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} +mod support; +pub use support::Support; diff --git a/crates/core/src/power_error.rs b/crates/core/src/power_error.rs index dc81cb3..9cccb3e 100644 --- a/crates/core/src/power_error.rs +++ b/crates/core/src/power_error.rs @@ -1,4 +1,22 @@ /// All possible errors returned by the [Power] trait. +#[derive(Debug)] 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 {} diff --git a/crates/core/src/power_trait.rs b/crates/core/src/power_trait.rs index be84514..79eeb28 100644 --- a/crates/core/src/power_trait.rs +++ b/crates/core/src/power_trait.rs @@ -1,5 +1,5 @@ use super::PowerError; -use super::Value; +use super::primitives::Value; /// Power control interface for a device pub trait Power { @@ -17,5 +17,5 @@ pub trait Power { pub trait PowerOp { /// Do the operations match? /// This should ignore values of the operation. - fn is_eq_op(&self, other: Self) -> bool; + fn is_eq_op(&self, other: &Self) -> bool; } diff --git a/crates/core/src/primitives/mod.rs b/crates/core/src/primitives/mod.rs new file mode 100644 index 0000000..39f69cf --- /dev/null +++ b/crates/core/src/primitives/mod.rs @@ -0,0 +1,7 @@ +//! Primitive types for power control values + +mod range; +pub use range::{Range, RangeList, RangeListParseErr, RangeListItem}; + +mod value; +pub use value::Value; diff --git a/crates/core/src/primitives/range.rs b/crates/core/src/primitives/range.rs new file mode 100644 index 0000000..e31d582 --- /dev/null +++ b/crates/core/src/primitives/range.rs @@ -0,0 +1,129 @@ +/// A combination of ranges of values and single items. +pub struct RangeList { + /// Elements of the list range + pub items: Vec>, +} + +/// An item of the range list +#[derive(PartialEq, Eq, Debug)] +pub enum RangeListItem { + /// A single item + Single(N), + /// A range of consecutive items + Range(Range), +} + +/// A range of values, usually inclusive. +#[derive(PartialEq, Eq, Debug)] +pub struct Range { + /// 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 { + /// The end of the string was encountered before parsing completed + UnexpectedEnd, + /// An item of the list failed to parse + Inner(E), +} + +impl From for RangeListParseErr { + fn from(value: E) -> Self { + RangeListParseErr::Inner(value) + } +} + +impl core::fmt::Display for RangeListParseErr { + 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 std::error::Error for RangeListParseErr {} + +impl core::str::FromStr for RangeList { + type Err = RangeListParseErr<::Err>; + fn from_str(s: &str) -> Result { + 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<::Err>> { + let parsed: RangeList = "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<::Err>> { + let parsed: RangeList = "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<::Err>> { + let parsed: RangeList = "42".parse()?; + assert_eq!(parsed.items, vec![RangeListItem::Single(42)]); + Ok(()) + } + + #[test] + fn parse_nothing() -> Result<(), RangeListParseErr<::Err>> { + let parsed: RangeList = "".parse()?; + assert_eq!(parsed.items, vec![]); + Ok(()) + } + + #[test] + fn parse_fail() { + let parsed: Result, _> = "1,2-".parse(); + assert!(parsed.is_err_and(|e| matches!(e, RangeListParseErr::UnexpectedEnd))) + } +} diff --git a/crates/core/src/primitives/value.rs b/crates/core/src/primitives/value.rs new file mode 100644 index 0000000..e996fdf --- /dev/null +++ b/crates/core/src/primitives/value.rs @@ -0,0 +1,162 @@ +/// Value of a power reading +pub enum Value> { + /// 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 { + 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 { + 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 { + 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 { + 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 std::convert::From for Value { + fn from(value: f64) -> Self { + Self::Float(value) + } +} + +impl std::convert::From for Value { + fn from(value: u64) -> Self { + Self::UInt(value) + } +} + +impl std::convert::From for Value { + fn from(value: i64) -> Self { + Self::Int(value) + } +} + +impl std::convert::From for Value { + fn from(value: bool) -> Self { + Self::Bool(value) + } +} + +impl std::convert::From for Value { + fn from(value: String) -> Self { + Self::String(value) + } +} + +impl std::convert::From<&str> for Value { + fn from(value: &str) -> Self { + Self::String(value.to_owned()) + } +} + +impl Value { + + /// Convert standard value types between generic variants. + /// Fails if trying to convert between custom types + pub fn maybe_convert(value: Value) -> Option { + match value { + Value::::Float(f) => Some(Value::::Float(f)), + Value::::UInt(u) => Some(Value::::UInt(u)), + Value::::Int(i) => Some(Value::::Int(i)), + Value::::Bool(b) => Some(Value::::Bool(b)), + Value::::String(s) => Some(Value::::String(s)), + Value::::Custom(_) => None, + Value::::Unknown => Some(Value::Unknown), + } + } +} + +impl Value { + /// Convert a specialised Value into a Value>. + pub fn into_any(value: Value) -> Self { + match value { + Value::::Float(f) => Value::Float(f), + Value::::UInt(u) => Value::UInt(u), + Value::::Int(i) => Value::Int(i), + Value::::Bool(b) => Value::Bool(b), + Value::::String(s) => Value::String(s), + Value::::Custom(c) => Value::Custom(Box::new(c)), + Value::::Unknown => Value::Unknown, + } + } +} + +/* If only this could not include the above types in C... +impl std::convert::From for Value { + fn from(value: C) -> Self { + Self::Custom(value) + } +} +*/ diff --git a/crates/core/src/support.rs b/crates/core/src/support.rs index dee4c9f..41f2a58 100644 --- a/crates/core/src/support.rs +++ b/crates/core/src/support.rs @@ -1,7 +1,11 @@ /// Support status of a power interface pub enum Support { + /// Only read operations are supported Get, + /// Only write operations are supported Set, + /// Read and write are supported GetSet, + /// Nothing is supported Unsupported, } diff --git a/crates/core/src/value.rs b/crates/core/src/value.rs deleted file mode 100644 index 2be1e22..0000000 --- a/crates/core/src/value.rs +++ /dev/null @@ -1,67 +0,0 @@ -/// Value of a power reading -pub enum Value> { - /// 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 { - 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 { - 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 { - 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(_)) - } -} diff --git a/crates/procbox/Cargo.toml b/crates/procbox/Cargo.toml index 73e0d6a..45aec04 100644 --- a/crates/procbox/Cargo.toml +++ b/crates/procbox/Cargo.toml @@ -7,3 +7,4 @@ description = "Power toolbox for processors" [dependencies] powerbox = { version = "0.1", path = "../core" } +sysfuss = { version = "0.3", path = "../../../sysfs-nav" } diff --git a/crates/procbox/src/cpu/basic.rs b/crates/procbox/src/cpu/basic.rs new file mode 100644 index 0000000..7fe973e --- /dev/null +++ b/crates/procbox/src/cpu/basic.rs @@ -0,0 +1,633 @@ +use powerbox::{Power, PowerOp}; +use powerbox::primitives::RangeList; +use sysfuss::{BasicEntityPath, SysEntityAttributesExt}; + +use super::{CpuPower, CpuPowerOp}; + +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::(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) -> 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>> for BasicCpus { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + Box::new( + Power::::supported_operations(self).map(core::convert::Into::::into) + .chain(Power::::supported_operations(self).map(core::convert::Into::::into)) + .chain(Power::::supported_operations(self).map(core::convert::Into::::into)) + .chain(Power::::supported_operations(self).map(core::convert::Into::::into)) + .chain(Power::::supported_operations(self).map(core::convert::Into::::into)) + .chain(Power::::supported_operations(self).map(core::convert::Into::::into)) + ) + } + + fn act(&self, op: BasicCpusOp) -> Result>, 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 for BasicCpus { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + Power::>>::supported_operations(self) + } + + fn act(&self, op: BasicCpusOp) -> Result { + Power::>>::act(self, op) + .map(powerbox::primitives::Value::into_any) + } +} + +impl CpuPower 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 for BasicCpus { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + 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 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::::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 for BasicCpus { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUS_SMT_ACTIVE) { Box::new(core::iter::once(GetSMT)) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, _: GetSMT) -> Result { + if self.sysfs.exists(&CPUS_SMT_ACTIVE) { + match self.sysfs.attribute::(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 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::::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 for BasicCpus { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + Box::new(Power::::supported_operations(self).map(core::convert::Into::::into) + .chain(Power::::supported_operations(self).map(core::convert::Into::::into))) + } + + fn act(&self, op: SMT) -> Result { + match op { + SMT::Get(get) => Power::::act(self, get).map(core::convert::Into::into), + SMT::Set(set) => Power::::act(self, set).map(|_| powerbox::primitives::Value::Unknown), + } + } +} + +impl core::convert::Into for SMT { + fn into(self) -> BasicCpusOp { + BasicCpusOp::SMT(self) + } +} + +pub struct GetPresent; // Option +impl PowerOp for GetPresent { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for GetPresent {} + +impl Power> for BasicCpus { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUS_PRESENT) { Box::new(core::iter::once(GetPresent)) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, _: GetPresent) -> Result, powerbox::PowerError> { + if self.sysfs.exists(&CPUS_PRESENT) { + self.sysfs.attribute::, _>(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 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::::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 +impl PowerOp for GetPossible { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for GetPossible {} + +impl Power> for BasicCpus { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUS_POSSIBLE) { Box::new(core::iter::once(GetPossible)) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, _: GetPossible) -> Result, powerbox::PowerError> { + if self.sysfs.exists(&CPUS_POSSIBLE) { + self.sysfs.attribute::, _>(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::::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 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 for BasicCpus { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUS_KERNEL_MAX) { Box::new(core::iter::once(GetKernelMax)) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, _: GetKernelMax) -> Result { + if self.sysfs.exists(&CPUS_KERNEL_MAX) { + self.sysfs.attribute::(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 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::::supported_operations(&target).count() == 1); + let kmax = target.act(GetKernelMax)?; + assert_eq!(kmax, 8191); + Ok(()) + } +} + +pub struct GetCpusOnline; // Option +impl PowerOp for GetCpusOnline { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for GetCpusOnline {} + +impl Power> for BasicCpus { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUS_ONLINE) { Box::new(core::iter::once(GetCpusOnline)) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, _: GetCpusOnline) -> Result, powerbox::PowerError> { + if self.sysfs.exists(&CPUS_ONLINE) { + self.sysfs.attribute::, _>(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 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::::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 +impl PowerOp for GetCpusOffline { + fn is_eq_op(&self, _: &Self) -> bool { + true + } +} + +impl CpuPowerOp for GetCpusOffline {} + +impl Power> for BasicCpus { + fn is_on(&self) -> bool { + self.is_online() + } + + fn is_available(&self) -> bool { + self.is_exists() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&CPUS_OFFLINE) { Box::new(core::iter::once(GetCpusOffline)) } else { Box::new(core::iter::empty()) } + } + + fn act(&self, _: GetCpusOffline) -> Result, powerbox::PowerError> { + if self.sysfs.exists(&CPUS_OFFLINE) { + self.sysfs.attribute::, _>(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 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::::supported_operations(&target).count() == 1); + let rl = target.act(GetCpusOffline)?; + assert_eq!(rl.items, vec![]); + Ok(()) + } +} + +/// General single CPU functionality +pub struct BasicCpu { + +} + +pub enum BasicCpuOp { + Online(Online), + CpuFreq(CpuFreq), + //CpuIdle(), + //ACPI(), +} + +pub struct SetCpuOnline(pub bool); +pub struct GetCpuOnline; +pub enum Online { + Get(GetCpuOnline), + Set(SetCpuOnline), +} + +pub struct GetAffectedCpus; // range or number?? +pub struct GetRelatedCpus; // range or number?? +pub enum CpuFreq { + AffectedCpus(GetAffectedCpus), + RelatedCpus(GetRelatedCpus), + CpuInfo(CpuInfo), + Energy(EnergyPerf), + Scaling(), +} + +pub struct GetCpuInfoMinFreq; // number +pub struct GetCpuInfoMaxFreq; // number +pub enum CpuInfo { + MinFreq(GetCpuInfoMinFreq), + MaxFreq(GetCpuInfoMaxFreq), +} + +pub struct GetEnergyPerfAvailablePreferences; // list of string +pub enum EnergyPerf { + AvailablePreferences(GetEnergyPerfAvailablePreferences), + Preference(EnergyPerfPreference), +} + +pub struct GetEnergyPerfPreference; // string +pub struct SetEnergyPerfPreference(String); +pub enum EnergyPerfPreference { + Get(GetEnergyPerfPreference), + Set(SetEnergyPerfPreference) +} + +pub struct GetScalingAvailableGovernors; // list of string +pub struct GetScalingCurFreq; // number +pub struct GetScalingDriver; // string +pub enum Scaling { + AvailableGovernors(GetScalingAvailableGovernors), + CurFreq(GetScalingCurFreq), + Driver(GetScalingDriver), + Governor(ScalingGovernor), + MaxFreq(ScalingMaxFreq), + MinFreq(ScalingMinFreq), + //Setspeed(), +} + +pub struct GetScalingGovernor; // string +pub struct SetScalingGovernor(String); +pub enum ScalingGovernor { + Get(GetScalingGovernor), + Set(SetScalingGovernor), +} + +pub struct GetScalingMaxFreq; // number +pub struct SetScalingMaxFreq(usize); +pub enum ScalingMaxFreq { + Get(GetScalingMaxFreq), + Set(SetScalingMaxFreq), +} + +pub struct GetScalingMinFreq; // number +pub struct SetScalingMinFreq(usize); +pub enum ScalingMinFreq { + Get(GetScalingMinFreq), + Set(SetScalingMinFreq), +} diff --git a/crates/procbox/src/cpu/cpu_trait.rs b/crates/procbox/src/cpu/cpu_trait.rs index 1a85075..de13d6a 100644 --- a/crates/procbox/src/cpu/cpu_trait.rs +++ b/crates/procbox/src/cpu/cpu_trait.rs @@ -1,5 +1,5 @@ /// Power control interface for a CPU -pub trait CpuPower: powerbox::Power {} +pub trait CpuPower: powerbox::Power {} /// Power control operation(s) of a CPU pub trait CpuPowerOp: powerbox::PowerOp {} diff --git a/crates/procbox/src/cpu/mod.rs b/crates/procbox/src/cpu/mod.rs index 77e2dbd..d4d5fe1 100644 --- a/crates/procbox/src/cpu/mod.rs +++ b/crates/procbox/src/cpu/mod.rs @@ -1,3 +1,7 @@ //! CPU power management interface mod cpu_trait; pub use cpu_trait::{CpuPower, CpuPowerOp}; + +#[allow(missing_docs)] +pub mod basic; +pub use basic::{BasicCpus, BasicCpu}; diff --git a/crates/procbox/src/gpu/gpu_trait.rs b/crates/procbox/src/gpu/gpu_trait.rs index 02545f1..a5143bd 100644 --- a/crates/procbox/src/gpu/gpu_trait.rs +++ b/crates/procbox/src/gpu/gpu_trait.rs @@ -1,5 +1,5 @@ /// Power control interface for a GPU -pub trait GpuPower: powerbox::Power {} +pub trait GpuPower: powerbox::Power {} /// Power control operation(s) of a GPU pub trait GpuPowerOp: powerbox::PowerOp {} diff --git a/crates/procbox/src/lib.rs b/crates/procbox/src/lib.rs index df886b3..94ed7e8 100644 --- a/crates/procbox/src/lib.rs +++ b/crates/procbox/src/lib.rs @@ -4,13 +4,4 @@ pub mod cpu; pub mod gpu; -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } -} +pub mod combo; diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 7d12d9a..0000000 --- a/src/lib.rs +++ /dev/null @@ -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); - } -} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..dbfbf5c --- /dev/null +++ b/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello powerbox-cli"); +}