Add basic and power supply sysfs classes
This commit is contained in:
parent
e4579b6998
commit
346341b804
9 changed files with 715 additions and 8 deletions
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "sysfs-nav"
|
||||
name = "sysfuss"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
|
59
src/attribute.rs
Normal file
59
src/attribute.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
use std::path::PathBuf;
|
||||
use std::io::Result as IoResult;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::EitherErr2;
|
||||
|
||||
/// sysfs entity attribute file functionality
|
||||
pub trait SysAttribute: Eq {
|
||||
/// attribute file name
|
||||
fn filename(&self) -> PathBuf;
|
||||
|
||||
/// full path to attribute file
|
||||
fn path(&self, entity: &dyn crate::SysEntity) -> PathBuf {
|
||||
entity.as_ref().join(self.filename())
|
||||
}
|
||||
|
||||
/// returns true if the path to the attribute file exists
|
||||
fn exists(&self, entity: &dyn crate::SysEntity) -> bool {
|
||||
let path = self.path(entity);
|
||||
path.exists() && path.is_file()
|
||||
}
|
||||
|
||||
/// read attribute value from sysfs
|
||||
fn read_value(&self, entity: &dyn crate::SysEntity) -> IoResult<Vec<u8>> {
|
||||
std::fs::read(self.path(entity))
|
||||
}
|
||||
|
||||
/// write attribute value to sysfs
|
||||
fn write_value(&self, entity: &dyn crate::SysEntity, value: &[u8]) -> IoResult<()> {
|
||||
std::fs::write(self.path(entity), value)
|
||||
}
|
||||
}
|
||||
|
||||
impl SysAttribute for String {
|
||||
fn filename(&self) -> PathBuf {
|
||||
PathBuf::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// sysfs entity attribute functionality extension
|
||||
pub trait SysAttributeExt: SysAttribute {
|
||||
/// read attribute as string
|
||||
fn read_str(&self, entity: &dyn crate::SysEntity) -> IoResult<String> {
|
||||
std::fs::read_to_string(self.path(entity))
|
||||
}
|
||||
|
||||
/// write attribute string value to sysfsfilter_map
|
||||
fn write_str(&self, entity: &dyn crate::SysEntity, s: &str) -> IoResult<()> {
|
||||
std::fs::write(self.path(entity), s.as_bytes())
|
||||
}
|
||||
|
||||
/// read and parse the attribute value
|
||||
fn parse<T: FromStr<Err=E>, E>(&self, entity: &dyn crate::SysEntity) -> Result<T, EitherErr2<std::io::Error, E>> {
|
||||
let s = self.read_str(entity).map_err(EitherErr2::First)?;
|
||||
T::from_str(&s).map_err(EitherErr2::Second)
|
||||
}
|
||||
}
|
||||
|
||||
impl <X: SysAttribute> SysAttributeExt for X {}
|
91
src/basic.rs
Normal file
91
src/basic.rs
Normal file
|
@ -0,0 +1,91 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::convert::AsRef;
|
||||
use std::io::Result as IoResult;
|
||||
|
||||
const SYS_CLASS_PATH: &str = "sys/class";
|
||||
|
||||
/// Generic entity path with basic functionality
|
||||
pub struct BasicEntityPath {
|
||||
path: PathBuf
|
||||
}
|
||||
|
||||
impl BasicEntityPath {
|
||||
/// Create a new BasicEntityPath for manipulating path
|
||||
pub fn new(path: impl AsRef<Path>) -> Self {
|
||||
Self {
|
||||
path: path.as_ref().to_path_buf(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn all(root: impl AsRef<Path>, class: impl AsRef<Path>) -> IoResult<impl Iterator<Item=IoResult<Self>>> {
|
||||
let dir_path = root.as_ref().join(SYS_CLASS_PATH).join(class);
|
||||
dir_path.read_dir()
|
||||
.map(
|
||||
|iter| iter.filter_map(
|
||||
|entry| entry.map(
|
||||
|entry| if entry.path().is_file() { None } else { Some(Self::new(entry.path())) }
|
||||
).transpose()))
|
||||
}
|
||||
|
||||
/// Filter out attributes that do not exist on this entity
|
||||
pub fn filter_capabilities<A: crate::SysAttribute>(&self, attributes: impl Iterator<Item=A>) -> impl Iterator<Item=A> {
|
||||
let found_attrs: Vec<PathBuf> = if let Ok(dir_iter) = self.path.read_dir() {
|
||||
dir_iter.filter_map(
|
||||
|entry| entry.ok().filter(|entry| entry.path().is_file()).map(|entry| PathBuf::from(entry.file_name()))
|
||||
).collect()
|
||||
} else {
|
||||
Vec::with_capacity(0)
|
||||
};
|
||||
|
||||
attributes.filter(move |attr| found_attrs.contains(&attr.filename()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Path> for BasicEntityPath {
|
||||
fn as_ref(&self) -> &Path {
|
||||
self.path.as_path()
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::SysEntity for BasicEntityPath {
|
||||
fn to_entity_path(self) -> crate::EntityPath {
|
||||
crate::EntityPath::Generic(self)
|
||||
}
|
||||
|
||||
fn name(&self) -> IoResult<String> {
|
||||
if let Some(s) = self.path.file_name().map(|name| name.to_str().map(|s| s.to_owned())).flatten() {
|
||||
Ok(s)
|
||||
} else {
|
||||
Err(std::io::Error::from_raw_os_error(std::io::ErrorKind::InvalidInput as _))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::SysEntityAttributes<String, String, <String as std::str::FromStr>::Err> for BasicEntityPath {
|
||||
fn capabilities(&self) -> Vec<String> {
|
||||
if let Ok(dir_iter) = self.path.read_dir() {
|
||||
dir_iter.filter_map(
|
||||
|entry| entry.ok().filter(|entry| entry.path().is_file()).and_then(|entry| entry.file_name().to_str().map(|s| s.to_owned()))
|
||||
).collect()
|
||||
} else {
|
||||
Vec::with_capacity(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::SysEntityRawExt;
|
||||
|
||||
#[test]
|
||||
fn basic_entity_all() -> std::io::Result<()> {
|
||||
let sys = crate::SysPath::default();
|
||||
let all_basic: Vec<_> = BasicEntityPath::all(sys, "drm")?.collect();
|
||||
assert!(!all_basic.is_empty());
|
||||
for ent in all_basic.into_iter() {
|
||||
assert!(ent?.attribute_str("uevent")? != "");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
107
src/entity.rs
Normal file
107
src/entity.rs
Normal file
|
@ -0,0 +1,107 @@
|
|||
use std::path::Path;
|
||||
use std::convert::AsRef;
|
||||
use std::io::Result as IoResult;
|
||||
|
||||
use crate::EitherErr2;
|
||||
use crate::SysAttributeExt;
|
||||
|
||||
/// sysfs class entity functionality
|
||||
pub trait SysEntity: AsRef<Path> {
|
||||
/// Convert specialized entity into general entity path container
|
||||
fn to_entity_path(self) -> EntityPath;
|
||||
|
||||
/// Get the entity's name
|
||||
fn name(&self) -> IoResult<String>;
|
||||
}
|
||||
|
||||
/// sysfs class entity functionality extension
|
||||
pub trait SysEntityRawExt: SysEntity {
|
||||
/// Get an attribute on the entity
|
||||
fn attribute<A: crate::SysAttribute, T: std::str::FromStr<Err=E>, E>(&self, attr: A) -> Result<T, EitherErr2<std::io::Error, E>>;
|
||||
/// Get an attribute by filename in the entity's directory
|
||||
fn attribute_str<A: AsRef<Path>>(&self, attr: A) -> IoResult<String>;
|
||||
|
||||
/// Returns true when the entity has the attribute
|
||||
fn exists<A: crate::SysAttribute>(&self, attr: A) -> bool;
|
||||
|
||||
/// Returns true when the entity has the attribute
|
||||
fn exists_str<A: AsRef<Path>>(&self, attr: A) -> bool;
|
||||
}
|
||||
|
||||
impl <X: SysEntity> SysEntityRawExt for X {
|
||||
fn attribute<A: crate::SysAttribute, T: std::str::FromStr<Err=E>, E>(&self, attr: A) -> Result<T, EitherErr2<std::io::Error, E>> {
|
||||
attr.parse(self)
|
||||
}
|
||||
|
||||
fn attribute_str<A: AsRef<Path>>(&self, attr: A) -> IoResult<String> {
|
||||
std::fs::read_to_string(self.as_ref().join(attr))
|
||||
}
|
||||
|
||||
fn exists_str<A: AsRef<Path>>(&self, attr: A) -> bool {
|
||||
self.as_ref().join(attr).exists()
|
||||
}
|
||||
|
||||
fn exists<A: crate::SysAttribute>(&self, attr: A) -> bool {
|
||||
attr.exists(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// sysfs class entity attribute type indicator
|
||||
pub trait SysEntityAttributes<A: crate::SysAttribute, T: std::str::FromStr<Err=E>, E>: SysEntity + Sized {
|
||||
/// Get an attribute on the entity
|
||||
fn attribute(&self, attr: A) -> Result<T, EitherErr2<std::io::Error, E>> {
|
||||
attr.parse(self)
|
||||
}
|
||||
|
||||
/// Get attributes available on this entity;
|
||||
fn capabilities(&self) -> Vec<A>;
|
||||
}
|
||||
|
||||
/*pub trait SysEntityAttributesExt<A: crate::SysAttribute, T: std::str::FromStr<Err=E>, E>: SysEntityAttributes<A, T, E> {
|
||||
fn attribute()
|
||||
}*/
|
||||
|
||||
/// sysfs class entity implementors
|
||||
pub enum EntityPath {
|
||||
/// hwmon
|
||||
HwMon(crate::HwMonPath),
|
||||
/// power_supply
|
||||
PowerSupply(crate::PowerSupplyPath),
|
||||
/// Generic
|
||||
Generic(crate::BasicEntityPath),
|
||||
/// Miscellaneous
|
||||
Custom(Box<dyn SysEntity>),
|
||||
}
|
||||
|
||||
impl EntityPath {
|
||||
fn inner(&self) -> &'_ dyn SysEntity {
|
||||
match self {
|
||||
Self::HwMon(inner) => inner,
|
||||
Self::PowerSupply(inner) => inner,
|
||||
Self::Generic(inner) => inner,
|
||||
Self::Custom(inner) => inner.as_ref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Path> for EntityPath {
|
||||
fn as_ref(&self) -> &Path {
|
||||
match self {
|
||||
Self::HwMon(inner) => inner.as_ref(),
|
||||
Self::PowerSupply(inner) => inner.as_ref(),
|
||||
Self::Generic(inner) => inner.as_ref(),
|
||||
Self::Custom(inner) => inner.as_ref().as_ref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SysEntity for EntityPath {
|
||||
fn to_entity_path(self) -> Self {
|
||||
self
|
||||
}
|
||||
|
||||
fn name(&self) -> IoResult<String> {
|
||||
self.inner().name()
|
||||
}
|
||||
}
|
47
src/errors.rs
Normal file
47
src/errors.rs
Normal file
|
@ -0,0 +1,47 @@
|
|||
/// Multi-error convenience enum
|
||||
#[derive(Debug)]
|
||||
pub enum EitherErr2<T1, T2> {
|
||||
/// Type 1
|
||||
First(T1),
|
||||
/// Type 2
|
||||
Second(T2),
|
||||
}
|
||||
|
||||
impl <T1, T2> EitherErr2<T1, T2> {
|
||||
/// Is this the first error type?
|
||||
pub fn is_type1(&self) -> bool {
|
||||
matches!(self, Self::First(_))
|
||||
}
|
||||
|
||||
/// Is this the second error type?
|
||||
pub fn is_type2(&self) -> bool {
|
||||
matches!(self, Self::Second(_))
|
||||
}
|
||||
}
|
||||
|
||||
impl <T1: std::fmt::Display, T2: std::fmt::Display> std::fmt::Display for EitherErr2<T1, T2> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::First(x) => write!(f, "{}", x),
|
||||
Self::Second(x) => write!(f, "{}", x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl <T1: std::error::Error, T2: std::error::Error> std::error::Error for EitherErr2<T1, T2> {}
|
||||
|
||||
/// Value parse error for an enumeration.
|
||||
/// This usually indicates an unexpected value (one without an enum variant) was received while parsing.
|
||||
#[derive(Debug)]
|
||||
pub struct ValueEnumError<'a> {
|
||||
pub(crate) got: String,
|
||||
pub(crate) allowed: &'a [&'a str],
|
||||
}
|
||||
|
||||
impl <'a> std::fmt::Display for ValueEnumError<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Got {}, expected one of {:?}", self.got, self.allowed)
|
||||
}
|
||||
}
|
||||
|
||||
impl <'a> std::error::Error for ValueEnumError<'a> {}
|
95
src/hwmon.rs
95
src/hwmon.rs
|
@ -6,12 +6,12 @@ const HWMON_DIR_PATH: &'static str = "sys/class/hwmon/";
|
|||
|
||||
/// Attribute representation.
|
||||
/// Attribute files are usually in the format `<type><number>_<item>`.
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct HwMonAttribute {
|
||||
inner: HwMonAttributeInner,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum HwMonAttributeInner {
|
||||
Name,
|
||||
Standard {
|
||||
|
@ -22,8 +22,30 @@ enum HwMonAttributeInner {
|
|||
Uevent,
|
||||
}
|
||||
|
||||
impl std::str::FromStr for HwMonAttributeInner {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"name" => Ok(Self::Name),
|
||||
"uevent" => Ok(Self::Uevent),
|
||||
s => {
|
||||
let start = HwMonAttributeType::from_attr_start(s)?;
|
||||
let end = HwMonAttributeItem::from_attr_end(s)?;
|
||||
let trimmed = s.trim_start_matches(start.to_attr_str()).trim_end_matches(end.to_attr_str());
|
||||
if trimmed.ends_with("_") {
|
||||
let index = trimmed.trim_end_matches('_').parse().map_err(|e| format!("Invalid number in attribute name {}: {}", s, e))?;
|
||||
Ok(Self::Standard { ty: start, number: index, item: end })
|
||||
} else {
|
||||
Err(format!("Missing underscore in attribute name {}", s))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attribute type in the format `<type><number>_<item>`
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum HwMonAttributeType {
|
||||
/// Voltage
|
||||
In,
|
||||
|
@ -36,7 +58,7 @@ pub enum HwMonAttributeType {
|
|||
}
|
||||
|
||||
impl HwMonAttributeType {
|
||||
fn to_attr_str(&self) -> &str {
|
||||
const fn to_attr_str(&self) -> &str {
|
||||
match self {
|
||||
Self::In => "in",
|
||||
Self::Temp => "out",
|
||||
|
@ -44,10 +66,24 @@ impl HwMonAttributeType {
|
|||
Self::Curr => "curr",
|
||||
}
|
||||
}
|
||||
|
||||
fn from_attr_start(s: &str) -> Result<Self, String> {
|
||||
if s.starts_with(Self::In.to_attr_str()) {
|
||||
Ok(Self::In)
|
||||
} else if s.starts_with(Self::Temp.to_attr_str()) {
|
||||
Ok(Self::Temp)
|
||||
} else if s.starts_with(Self::Fan.to_attr_str()) {
|
||||
Ok(Self::Fan)
|
||||
} else if s.starts_with(Self::Curr.to_attr_str()) {
|
||||
Ok(Self::Curr)
|
||||
} else {
|
||||
Err(format!("Unrecognised hwmon attribute type in name {}", s))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attribute item in the format `<type><number>_<item>`
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum HwMonAttributeItem {
|
||||
/// Input
|
||||
Input,
|
||||
|
@ -58,13 +94,25 @@ pub enum HwMonAttributeItem {
|
|||
}
|
||||
|
||||
impl HwMonAttributeItem {
|
||||
fn to_attr_str(&self) -> &str {
|
||||
const fn to_attr_str(&self) -> &str {
|
||||
match self {
|
||||
Self::Input => "input",
|
||||
Self::Min => "min",
|
||||
Self::Max => "max",
|
||||
}
|
||||
}
|
||||
|
||||
fn from_attr_end(s: &str) -> Result<Self, String> {
|
||||
if s.ends_with(Self::Input.to_attr_str()) {
|
||||
Ok(Self::Input)
|
||||
} else if s.ends_with(Self::Min.to_attr_str()) {
|
||||
Ok(Self::Min)
|
||||
} else if s.ends_with(Self::Max.to_attr_str()) {
|
||||
Ok(Self::Max)
|
||||
} else {
|
||||
Err(format!("Unrecognised hwmon attribute type in name {}", s))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HwMonAttribute {
|
||||
|
@ -97,6 +145,13 @@ impl HwMonAttribute {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::SysAttribute for HwMonAttribute {
|
||||
fn filename(&self) -> PathBuf {
|
||||
PathBuf::from(self.to_attr_str())
|
||||
}
|
||||
}
|
||||
|
||||
/// hwmon<number>/ directory
|
||||
pub struct HwMonPath {
|
||||
path: PathBuf
|
||||
|
@ -161,10 +216,33 @@ impl AsRef<Path> for HwMonPath {
|
|||
}
|
||||
}
|
||||
|
||||
impl crate::SysEntity for HwMonPath {
|
||||
fn to_entity_path(self) -> crate::EntityPath {
|
||||
crate::EntityPath::HwMon(self)
|
||||
}
|
||||
|
||||
fn name(&self) -> IoResult<String> {
|
||||
self.attribute(HwMonAttribute::name())
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::SysEntityAttributes<HwMonAttribute, String, <String as std::str::FromStr>::Err> for HwMonPath {
|
||||
fn capabilities(&self) -> Vec<HwMonAttribute> {
|
||||
if let Ok(dir_iter) = self.path.read_dir() {
|
||||
dir_iter.filter_map(
|
||||
|entry| entry.ok().filter(|entry| entry.path().is_file()).and_then(|entry| entry.file_name().into_string().ok()).and_then(|s| s.parse::<HwMonAttributeInner>().ok())
|
||||
).map(|inner| HwMonAttribute {inner}).collect()
|
||||
} else {
|
||||
Vec::with_capacity(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::SysEntityAttributes;
|
||||
|
||||
#[test]
|
||||
fn hwmon_all() -> std::io::Result<()> {
|
||||
|
@ -172,7 +250,10 @@ mod tests {
|
|||
let all_hwmon: Vec<_> = HwMonPath::all(sys)?.collect();
|
||||
assert!(!all_hwmon.is_empty());
|
||||
for hwmon in all_hwmon.into_iter() {
|
||||
assert!(hwmon?.attribute(HwMonAttribute::name())? != "");
|
||||
let hwmon = hwmon?;
|
||||
assert!(hwmon.attribute(HwMonAttribute::name())? != "");
|
||||
assert!(!hwmon.capabilities().is_empty());
|
||||
assert!(hwmon.capabilities().contains(&HwMonAttribute::name()))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
15
src/lib.rs
15
src/lib.rs
|
@ -4,9 +4,24 @@
|
|||
|
||||
pub(crate) const DEFAULT_ROOT: &'static str = "/";
|
||||
|
||||
mod attribute;
|
||||
pub use attribute::{SysAttribute, SysAttributeExt};
|
||||
|
||||
mod basic;
|
||||
pub use basic::BasicEntityPath;
|
||||
|
||||
mod entity;
|
||||
pub use entity::{EntityPath, SysEntity, SysEntityRawExt, SysEntityAttributes};
|
||||
|
||||
mod errors;
|
||||
pub use errors::{EitherErr2, ValueEnumError};
|
||||
|
||||
mod hwmon;
|
||||
pub use hwmon::{HwMonPath, HwMonAttribute, HwMonAttributeType, HwMonAttributeItem};
|
||||
|
||||
mod power_supply;
|
||||
pub use power_supply::{PowerSupplyPath, PowerSupplyType, PowerSupplyAttribute};
|
||||
|
||||
mod syspath;
|
||||
pub use syspath::SysPath;
|
||||
|
||||
|
|
292
src/power_supply.rs
Normal file
292
src/power_supply.rs
Normal file
|
@ -0,0 +1,292 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::convert::AsRef;
|
||||
use std::io::Result as IoResult;
|
||||
|
||||
use crate::SysEntityRawExt;
|
||||
//use crate::SysEntityAttributes;
|
||||
|
||||
const PSU_DIR_PATH: &'static str = "sys/class/power_supply/";
|
||||
|
||||
const CLASS_PATH: &'static str = "power_supply";
|
||||
|
||||
/// Attribute files in a power supply directory
|
||||
/// These attributes are taken from https://github.com/torvalds/linux/blob/master/Documentation/ABI/testing/sysfs-class-power
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum PowerSupplyAttribute {
|
||||
/// Reports the name of the device manufacturer.
|
||||
Manufacturer,
|
||||
/// Reports the name of the device model.
|
||||
ModelName,
|
||||
/// Reports the serial number of the device.
|
||||
SerialNumber,
|
||||
/// Describes the main type of the supply.
|
||||
Type,
|
||||
/// Reports an average current reading over a fixed period (uA).
|
||||
CurrentAverage,
|
||||
/// Reports the maximum current the power supply supports (uA).
|
||||
CurrentMax,
|
||||
/// Reports the instantaneous current of the power supply (uA).
|
||||
CurrentNow,
|
||||
/// Reports the temperature reading (dC -- 1/10 degree Celcius).
|
||||
Temperature,
|
||||
/// Maximum temperature where the supply will notify user-space of the event (dC).
|
||||
TemperatureAlertMax,
|
||||
/// Minimum temperature where the supply will notify user-space of the event (dC).
|
||||
TemperatureAlertMin,
|
||||
/// Reports the maximum allowed temperature for operation (dC).
|
||||
TemperatureMax,
|
||||
/// Reports the minimum allowed temperature for operation (dC).
|
||||
TemperatureMin,
|
||||
/// Reports the maximum safe voltage (uV).
|
||||
VoltageMax,
|
||||
/// Reports the minimum safe voltage (uV).
|
||||
VoltageMin,
|
||||
/// Reports the instantaneous voltage of the power supply (uV).
|
||||
VoltageNow,
|
||||
/// Fine grain representation of the battery capacity (%).
|
||||
Capacity,
|
||||
/// Maximum battery capacity trip-wire value where the supply will notify user-space of the event (%).
|
||||
CapacityAlertMax,
|
||||
/// Minimum battery capacity trip-wire value where the supply will notify user-space of the event (%).
|
||||
CapacityAlertMin,
|
||||
/// This values provides the maximum error margin expected to exist by the fuel gauge in percent (%).
|
||||
CapacityErrorMargin,
|
||||
/// Coarse representation of battery capacity ("Unknown", "Critical", "Low", "Normal", "High", or "Full").
|
||||
CapacityLevel,
|
||||
/// Maximum allowable charging current (uA).
|
||||
ChargeControlLimit,
|
||||
/// Maximum legal value for the charge_control_limit property (uA).
|
||||
ChargeControlLimitMax,
|
||||
/// Represents a battery percentage level, below which charging will begin (%).
|
||||
ChargeControlStartThreshold,
|
||||
/// Represents a battery percentage level, above which charging will stop (%).
|
||||
ChargeControlEndThreshold,
|
||||
/// Represents the type of charging currently being applied to the battery ("Unknown", "N/A", "Trickle", "Fast", "Standard", "Adaptive", "Custom", "Long Life", or "Bypass").
|
||||
ChargeType,
|
||||
/// Reports the charging current value which is used to determine when the battery is considered full and charging should end (uA).
|
||||
ChargeTermCurrent,
|
||||
/// Reports the health of the battery or battery side of charger functionality ( "Unknown", "Good", "Overheat", "Dead", "Over voltage", "Unspecified failure", "Cold", "Watchdog timer expire", "Safety timer expire", "Over current", "Calibration required", "Warm", "Cool", "Hot", or "No battery").
|
||||
Health,
|
||||
/// Reports the charging current applied during pre-charging phase for a battery charge cycle (uA).
|
||||
PrechargeCurrent,
|
||||
/// Reports whether a battery is present or not in the system (0 or 1). If the property does not exist, the battery is considered to be present.
|
||||
Present,
|
||||
/// Represents the charging status of the battery ( "Unknown", "Charging", "Discharging", "Not charging", or "Full").
|
||||
Status,
|
||||
/// Represents the charging behaviour ("auto", "inhibit-charge", or "force-discharge").
|
||||
ChargeBehaviour,
|
||||
/// Describes the battery technology supported by the supply.
|
||||
Technology,
|
||||
/// Reports an average voltage over a fixed period (uV).
|
||||
VoltageAverage,
|
||||
/// Reports the number of full charge + discharge cycles the battery has undergone.
|
||||
CycleCount,
|
||||
// TODO USB properties
|
||||
}
|
||||
|
||||
impl crate::SysAttribute for PowerSupplyAttribute {
|
||||
fn filename(&self) -> PathBuf {
|
||||
let s = match self {
|
||||
Self::Manufacturer => "manufacturer",
|
||||
Self::ModelName => "model_name",
|
||||
Self::SerialNumber => "serial_number",
|
||||
Self::Type => "type",
|
||||
Self::CurrentAverage => "current_avg",
|
||||
Self::CurrentMax => "current_max",
|
||||
Self::CurrentNow => "current_now",
|
||||
Self::Temperature => "temp",
|
||||
Self::TemperatureAlertMax => "temp_alert_max",
|
||||
Self::TemperatureAlertMin => "temp_alert_min",
|
||||
Self::TemperatureMax => "temp_max",
|
||||
Self::TemperatureMin => "temp_min",
|
||||
Self::VoltageMax => "voltage_max",
|
||||
Self::VoltageMin => "voltage_min",
|
||||
Self::VoltageNow => "voltage_now",
|
||||
Self::Capacity => "capacity",
|
||||
Self::CapacityAlertMax => "capacity_alert_max",
|
||||
Self::CapacityAlertMin => "capacity_alert_min",
|
||||
Self::CapacityErrorMargin => "capacity_error_margin",
|
||||
Self::CapacityLevel => "capacity_level",
|
||||
Self::ChargeControlLimit => "charge_control_limit",
|
||||
Self::ChargeControlLimitMax => "charge_control_limit_max",
|
||||
Self::ChargeControlStartThreshold => "charge_control_start_threshold",
|
||||
Self::ChargeControlEndThreshold => "charge_control_end_threshold",
|
||||
Self::ChargeType => "charge_type",
|
||||
Self::ChargeTermCurrent => "charge_term_current",
|
||||
Self::Health => "health",
|
||||
Self::PrechargeCurrent => "precharge_current",
|
||||
Self::Present => "present",
|
||||
Self::Status => "status",
|
||||
Self::ChargeBehaviour => "charge_behaviour",
|
||||
Self::Technology => "technology",
|
||||
Self::VoltageAverage => "voltage_avg",
|
||||
Self::CycleCount => "cycle_count",
|
||||
};
|
||||
s.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// power_supply/<entity>/type attribute value
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum PowerSupplyType {
|
||||
/// Battery
|
||||
Battery,
|
||||
/// UPS
|
||||
UPS,
|
||||
/// Mains
|
||||
Mains,
|
||||
/// USB
|
||||
USB,
|
||||
/// Wireless
|
||||
Wireless,
|
||||
}
|
||||
|
||||
impl PowerSupplyType {
|
||||
fn allowed_values() -> &'static [&'static str] {
|
||||
&[
|
||||
"Battery",
|
||||
"UPS",
|
||||
"Mains",
|
||||
"USB",
|
||||
"Wireless",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for PowerSupplyType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Battery => write!(f, "Battery"),
|
||||
Self::UPS => write!(f, "UPS"),
|
||||
Self::Mains => write!(f, "Mains"),
|
||||
Self::USB => write!(f, "USB"),
|
||||
Self::Wireless => write!(f, "Wireless")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for PowerSupplyType {
|
||||
type Err = crate::ValueEnumError<'static>;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"Battery" => Ok(Self::Battery),
|
||||
"UPS" => Ok(Self::UPS),
|
||||
"Mains" => Ok(Self::Mains),
|
||||
"USB" => Ok(Self::USB),
|
||||
"Wireless" => Ok(Self::Wireless),
|
||||
other => Err(crate::ValueEnumError {
|
||||
got: other.to_owned(),
|
||||
allowed: Self::allowed_values(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// power_supply/<entity>/ directory
|
||||
pub struct PowerSupplyPath {
|
||||
inner: crate::BasicEntityPath,
|
||||
}
|
||||
|
||||
impl PowerSupplyPath {
|
||||
pub(crate) fn all(root: impl AsRef<Path>) -> IoResult<impl Iterator<Item=IoResult<Self>>> {
|
||||
Ok(crate::BasicEntityPath::all(root, CLASS_PATH)?
|
||||
.map(|result| result.map(|inner| Self { inner })))
|
||||
}
|
||||
|
||||
/// Get a power_supply entry by directory name
|
||||
pub(crate) fn name(root: &crate::SysPath, name: &str) -> Self {
|
||||
Self {
|
||||
inner: crate::BasicEntityPath::new(root.as_ref().join(PSU_DIR_PATH).join(name))
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the power supply type
|
||||
pub fn type_str(&self) -> IoResult<String> {
|
||||
self.attribute_str("type")
|
||||
}
|
||||
|
||||
/// Get the power supply type
|
||||
pub fn type_enum(&self) -> Result<PowerSupplyType, crate::EitherErr2<std::io::Error, <PowerSupplyType as std::str::FromStr>::Err>> {
|
||||
self.attribute(PowerSupplyAttribute::Type)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl AsRef<Path> for PowerSupplyPath {
|
||||
fn as_ref(&self) -> &Path {
|
||||
self.inner.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::SysEntity for PowerSupplyPath {
|
||||
fn to_entity_path(self) -> crate::EntityPath {
|
||||
crate::EntityPath::PowerSupply(self)
|
||||
}
|
||||
|
||||
fn name(&self) -> IoResult<String> {
|
||||
self.inner.name()
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::SysEntityAttributes<PowerSupplyAttribute, String, <String as std::str::FromStr>::Err> for PowerSupplyPath {
|
||||
fn capabilities(&self) -> Vec<PowerSupplyAttribute> {
|
||||
self.inner.filter_capabilities(vec![
|
||||
PowerSupplyAttribute::Manufacturer,
|
||||
PowerSupplyAttribute::ModelName,
|
||||
PowerSupplyAttribute::SerialNumber,
|
||||
PowerSupplyAttribute::Type,
|
||||
PowerSupplyAttribute::CurrentAverage,
|
||||
PowerSupplyAttribute::CurrentMax,
|
||||
PowerSupplyAttribute::CurrentNow,
|
||||
PowerSupplyAttribute::Temperature,
|
||||
PowerSupplyAttribute::TemperatureAlertMax,
|
||||
PowerSupplyAttribute::TemperatureAlertMin,
|
||||
PowerSupplyAttribute::TemperatureMax,
|
||||
PowerSupplyAttribute::TemperatureMin,
|
||||
PowerSupplyAttribute::VoltageMax,
|
||||
PowerSupplyAttribute::VoltageMin,
|
||||
PowerSupplyAttribute::VoltageNow,
|
||||
PowerSupplyAttribute::Capacity,
|
||||
PowerSupplyAttribute::CapacityAlertMax,
|
||||
PowerSupplyAttribute::CapacityAlertMin,
|
||||
PowerSupplyAttribute::CapacityErrorMargin,
|
||||
PowerSupplyAttribute::CapacityLevel,
|
||||
PowerSupplyAttribute::ChargeControlLimit,
|
||||
PowerSupplyAttribute::ChargeControlLimitMax,
|
||||
PowerSupplyAttribute::ChargeControlStartThreshold,
|
||||
PowerSupplyAttribute::ChargeControlEndThreshold,
|
||||
PowerSupplyAttribute::ChargeType,
|
||||
PowerSupplyAttribute::ChargeTermCurrent,
|
||||
PowerSupplyAttribute::Health,
|
||||
PowerSupplyAttribute::PrechargeCurrent,
|
||||
PowerSupplyAttribute::Present,
|
||||
PowerSupplyAttribute::Status,
|
||||
PowerSupplyAttribute::ChargeBehaviour,
|
||||
PowerSupplyAttribute::Technology,
|
||||
PowerSupplyAttribute::VoltageAverage,
|
||||
PowerSupplyAttribute::CycleCount,
|
||||
].into_iter()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::SysEntityAttributes;
|
||||
|
||||
#[test]
|
||||
fn power_supply_all() -> std::io::Result<()> {
|
||||
let sys = crate::SysPath::default();
|
||||
let all_psu: Vec<_> = PowerSupplyPath::all(sys)?.collect();
|
||||
assert!(!all_psu.is_empty());
|
||||
for p in all_psu.into_iter() {
|
||||
let p = p?;
|
||||
assert!(p.type_str()? != "");
|
||||
assert!(!p.capabilities().is_empty());
|
||||
assert!(p.capabilities().contains(&PowerSupplyAttribute::Type))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
use std::convert::AsRef;
|
||||
|
||||
use crate::SysEntity;
|
||||
|
||||
/// sysfs root
|
||||
pub struct SysPath {
|
||||
root: PathBuf,
|
||||
|
@ -15,6 +17,14 @@ impl SysPath {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get all entities in the system
|
||||
pub fn entities(&self) -> std::io::Result<impl Iterator<Item=std::io::Result<crate::EntityPath>> + '_> {
|
||||
Ok(crate::HwMonPath::all(self)?
|
||||
.map(|entity| entity.map(|entity| entity.to_entity_path()))
|
||||
.chain(crate::PowerSupplyPath::all(self)?
|
||||
.map(|entity| entity.map(|entity| entity.to_entity_path()))))
|
||||
}
|
||||
|
||||
/// Find a hardware monitor entry by name
|
||||
pub fn hwmon_by_name(&self, name: &str) -> std::io::Result<crate::HwMonPath> {
|
||||
match crate::HwMonPath::name(self, name) {
|
||||
|
@ -30,6 +40,11 @@ impl SysPath {
|
|||
entry.attribute(crate::HwMonAttribute::name())?;
|
||||
Ok(entry)
|
||||
}
|
||||
|
||||
/// Find a power supply entry by name
|
||||
pub fn power_supply(&self, name: &str) -> crate::PowerSupplyPath {
|
||||
crate::PowerSupplyPath::name(self, name)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::default::Default for SysPath {
|
||||
|
|
Loading…
Reference in a new issue