Add basic components, CPUs interface (not single CPU yet)

This commit is contained in:
NGnius (Graham) 2024-06-14 21:08:13 -04:00
parent 3c466caf07
commit bafaccd54d
18 changed files with 973 additions and 108 deletions

View file

@ -1,5 +1,7 @@
[package]
name = "powerbox-cli"
version = "0.1.0"
edition = "2021"
[workspace.package]
version = "0.1.0"

View file

@ -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" }

View file

@ -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;

View file

@ -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 {}

View file

@ -1,5 +1,5 @@
use super::PowerError;
use super::Value;
use super::primitives::Value;
/// Power control interface for a device
pub trait Power<OP: PowerOp, VAL = Value> {
@ -17,5 +17,5 @@ pub trait Power<OP: PowerOp, VAL = Value> {
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;
}

View file

@ -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;

View file

@ -0,0 +1,129 @@
/// 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 <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)))
}
}

View 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)
}
}
*/

View file

@ -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,
}

View file

@ -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(_))
}
}

View file

@ -7,3 +7,4 @@ description = "Power toolbox for processors"
[dependencies]
powerbox = { version = "0.1", path = "../core" }
sysfuss = { version = "0.3", path = "../../../sysfs-nav" }

View file

@ -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::<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(())
}
}
/// 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),
}

View file

@ -1,5 +1,5 @@
/// 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
pub trait CpuPowerOp: powerbox::PowerOp {}

View file

@ -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};

View file

@ -1,5 +1,5 @@
/// 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
pub trait GpuPowerOp: powerbox::PowerOp {}

View file

@ -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;

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);
}
}

3
src/main.rs Normal file
View file

@ -0,0 +1,3 @@
fn main() {
println!("Hello powerbox-cli");
}