Define complete Rust back-end API and functionality
This commit is contained in:
parent
20ce2f1d5f
commit
c225554f78
30 changed files with 1523 additions and 89 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -42,3 +42,4 @@ yalc.lock
|
||||||
|
|
||||||
# rust
|
# rust
|
||||||
/powertools-rs/target
|
/powertools-rs/target
|
||||||
|
/bin
|
||||||
|
|
113
powertools-rs/Cargo.lock
generated
113
powertools-rs/Cargo.lock
generated
|
@ -2,6 +2,42 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aead"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes"
|
||||||
|
version = "0.7.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cipher",
|
||||||
|
"cpufeatures",
|
||||||
|
"opaque-debug",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes-gcm-siv"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589c637f0e68c877bbd59a4599bbe849cac8e5f3e4b5a3ebae8f528cd218dcdc"
|
||||||
|
dependencies = [
|
||||||
|
"aead",
|
||||||
|
"aes",
|
||||||
|
"cipher",
|
||||||
|
"ctr",
|
||||||
|
"polyval",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -66,6 +102,15 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cipher"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
@ -85,6 +130,15 @@ dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ctr"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea"
|
||||||
|
dependencies = [
|
||||||
|
"cipher",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
@ -251,6 +305,12 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hex"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "0.2.8"
|
version = "0.2.8"
|
||||||
|
@ -437,6 +497,12 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "obfstr"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b2b2cbbfd8defa51ff24450a61d73b3ff3e158484ddd274a883e886e6fbaa78"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.13.0"
|
version = "1.13.0"
|
||||||
|
@ -487,9 +553,21 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "polyval"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"opaque-debug",
|
||||||
|
"universal-hash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "powertools-rs"
|
name = "powertools-rs"
|
||||||
version = "0.1.0"
|
version = "1.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -691,6 +769,12 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.98"
|
version = "1.0.98"
|
||||||
|
@ -945,6 +1029,16 @@ dependencies = [
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "universal-hash"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.2.2"
|
version = "2.2.2"
|
||||||
|
@ -959,11 +1053,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "usdpl-back"
|
name = "usdpl-back"
|
||||||
version = "0.5.3"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6d237439986405621b9b6da350aefcfca9e2b808c10695f55f8b80ccc324b2c0"
|
checksum = "cbbc0781e83ba990f8239142e33173a2d2548701775f3db66702d1af4fd0319a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"hex",
|
||||||
|
"obfstr",
|
||||||
"tokio",
|
"tokio",
|
||||||
"usdpl-core",
|
"usdpl-core",
|
||||||
"warp",
|
"warp",
|
||||||
|
@ -971,10 +1067,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "usdpl-core"
|
name = "usdpl-core"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5dd726b9f0121d4449082e3ce73586dea0a0448494031833b7b173e4476f0ea5"
|
checksum = "862153581fac266458521f49e5906a71c1eee1665cb4c7d71e9586bd34b45394"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"aes-gcm-siv",
|
||||||
"base64",
|
"base64",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1109,3 +1206,9 @@ name = "windows_x86_64_msvc"
|
||||||
version = "0.36.1"
|
version = "0.36.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"
|
||||||
|
|
|
@ -1,15 +1,21 @@
|
||||||
[package]
|
[package]
|
||||||
name = "powertools-rs"
|
name = "powertools-rs"
|
||||||
version = "0.1.0"
|
version = "1.0.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# 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]
|
||||||
usdpl-back = { version = "0.5.3", features = ["blocking"]}
|
usdpl-back = { version = "0.6.0", features = ["blocking"]}
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
|
||||||
# logging
|
# logging
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
simplelog = "0.12"
|
simplelog = "0.12"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
decky = ["usdpl-back/decky"]
|
||||||
|
crankshaft = ["usdpl-back/crankshaft"]
|
||||||
|
encrypt = ["usdpl-back/encrypt"]
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
cargo build --release
|
cargo build --release --target x86_64-unknown-linux-musl
|
||||||
mkdir ../bin
|
mkdir ../bin &> /dev/null
|
||||||
# TODO replace "backend" \/ with binary name
|
cp ./target/release/powertools-rs ../bin/backend
|
||||||
cp ./target/release/backend ../bin/backend
|
|
||||||
|
|
64
powertools-rs/src/api/battery.rs
Normal file
64
powertools-rs/src/api/battery.rs
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
use std::sync::{mpsc::Sender, Arc, Mutex};
|
||||||
|
use usdpl_back::core::serdes::Primitive;
|
||||||
|
|
||||||
|
use crate::settings::{Battery, OnSet};
|
||||||
|
use crate::utility::{unwrap_lock, unwrap_maybe_fatal};
|
||||||
|
|
||||||
|
/// Current current (ha!) web method
|
||||||
|
pub fn current_now(_: super::ApiParameterType) -> super::ApiParameterType {
|
||||||
|
super::utility::map_result(crate::settings::Battery::current_now())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate set battery charge rate web method
|
||||||
|
pub fn set_charge_rate(
|
||||||
|
settings: Arc<Mutex<Battery>>,
|
||||||
|
saver: Sender<()>,
|
||||||
|
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||||
|
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||||
|
move |params_in: super::ApiParameterType| {
|
||||||
|
if let Some(Primitive::F64(new_val)) = params_in.get(0) {
|
||||||
|
let mut settings_lock = unwrap_lock(settings.lock(), "battery");
|
||||||
|
settings_lock.charge_rate = Some(*new_val as _);
|
||||||
|
unwrap_maybe_fatal(
|
||||||
|
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||||
|
"Failed to send on save channel",
|
||||||
|
);
|
||||||
|
super::utility::map_empty_result(
|
||||||
|
settings_lock.on_set(),
|
||||||
|
settings_lock.charge_rate.unwrap(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
vec!["set_charge_rate missing parameter".into()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate get battery charge rate web method
|
||||||
|
pub fn get_charge_rate(
|
||||||
|
settings: Arc<Mutex<Battery>>,
|
||||||
|
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||||
|
move |_: super::ApiParameterType| {
|
||||||
|
let settings_lock = unwrap_lock(settings.lock(), "battery");
|
||||||
|
vec![settings_lock
|
||||||
|
.charge_rate
|
||||||
|
.map(|x| x.into())
|
||||||
|
.unwrap_or(Primitive::Empty)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate unset battery charge rate web method
|
||||||
|
pub fn unset_charge_rate(
|
||||||
|
settings: Arc<Mutex<Battery>>,
|
||||||
|
saver: Sender<()>,
|
||||||
|
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||||
|
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||||
|
move |_: super::ApiParameterType| {
|
||||||
|
let mut settings_lock = unwrap_lock(settings.lock(), "battery");
|
||||||
|
settings_lock.charge_rate = None;
|
||||||
|
unwrap_maybe_fatal(
|
||||||
|
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||||
|
"Failed to send on save channel",
|
||||||
|
);
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
194
powertools-rs/src/api/cpu.rs
Normal file
194
powertools-rs/src/api/cpu.rs
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
use std::sync::{mpsc::Sender, Arc, Mutex};
|
||||||
|
use usdpl_back::core::serdes::Primitive;
|
||||||
|
|
||||||
|
use crate::settings::{Cpu, OnSet, SettingError, SettingVariant, MinMax};
|
||||||
|
use crate::utility::{unwrap_lock, unwrap_maybe_fatal};
|
||||||
|
|
||||||
|
/// Available CPUs web method
|
||||||
|
pub fn max_cpus(_: super::ApiParameterType) -> super::ApiParameterType {
|
||||||
|
super::utility::map_result(
|
||||||
|
Cpu::cpu_count()
|
||||||
|
.map(|x| x as u64)
|
||||||
|
.ok_or_else(
|
||||||
|
|| SettingError {
|
||||||
|
msg: "Failed to parse CPU count".to_owned(),
|
||||||
|
setting: SettingVariant::Cpu,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate set CPU online web method
|
||||||
|
pub fn set_cpu_online(
|
||||||
|
settings: Arc<Mutex<Vec<Cpu>>>,
|
||||||
|
saver: Sender<()>,
|
||||||
|
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||||
|
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||||
|
move |params_in: super::ApiParameterType| {
|
||||||
|
if let Some(Primitive::F64(index)) = params_in.get(0) {
|
||||||
|
let mut settings_lock = unwrap_lock(settings.lock(), "cpu");
|
||||||
|
if let Some(cpu) = settings_lock.get_mut(*index as usize) {
|
||||||
|
if let Some(Primitive::Bool(online)) = params_in.get(1) {
|
||||||
|
cpu.online = *online;
|
||||||
|
unwrap_maybe_fatal(
|
||||||
|
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||||
|
"Failed to send on save channel",
|
||||||
|
);
|
||||||
|
super::utility::map_empty_result(
|
||||||
|
cpu.on_set(),
|
||||||
|
cpu.online,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
vec!["set_cpu_online missing parameter 1".into()]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec!["set_cpu_online cpu index out of bounds".into()]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec!["set_cpu_online missing parameter 0".into()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_cpus_online(
|
||||||
|
settings: Arc<Mutex<Vec<Cpu>>>,
|
||||||
|
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||||
|
move |_: super::ApiParameterType| {
|
||||||
|
let settings_lock = unwrap_lock(settings.lock(), "cpu");
|
||||||
|
let mut output = Vec::with_capacity(settings_lock.len());
|
||||||
|
for cpu in settings_lock.as_slice() {
|
||||||
|
output.push(cpu.online.into());
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_clock_limits(
|
||||||
|
settings: Arc<Mutex<Vec<Cpu>>>,
|
||||||
|
saver: Sender<()>,
|
||||||
|
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||||
|
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||||
|
move |params_in: super::ApiParameterType| {
|
||||||
|
if let Some(Primitive::F64(index)) = params_in.get(0) {
|
||||||
|
let mut settings_lock = unwrap_lock(settings.lock(), "cpu");
|
||||||
|
if let Some(cpu) = settings_lock.get_mut(*index as usize) {
|
||||||
|
if let Some(Primitive::F64(min)) = params_in.get(1) {
|
||||||
|
if let Some(Primitive::F64(max)) = params_in.get(2) {
|
||||||
|
cpu.clock_limits = Some(MinMax {
|
||||||
|
min: *min as _,
|
||||||
|
max: *max as _,
|
||||||
|
});
|
||||||
|
unwrap_maybe_fatal(
|
||||||
|
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||||
|
"Failed to send on save channel",
|
||||||
|
);
|
||||||
|
match cpu.on_set() {
|
||||||
|
Ok(_) => vec![
|
||||||
|
cpu.clock_limits.as_ref().unwrap().min.into(),
|
||||||
|
cpu.clock_limits.as_ref().unwrap().max.into(),
|
||||||
|
],
|
||||||
|
Err(e) => vec![e.msg.into()]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec!["set_clock_limits missing parameter 2".into()]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec!["set_clock_limits missing parameter 1".into()]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec!["set_clock_limits cpu index out of bounds".into()]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec!["set_clock_limits missing parameter 0".into()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_clock_limits(
|
||||||
|
settings: Arc<Mutex<Vec<Cpu>>>,
|
||||||
|
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||||
|
move |params_in: super::ApiParameterType| {
|
||||||
|
if let Some(Primitive::F64(index)) = params_in.get(0) {
|
||||||
|
let mut settings_lock = unwrap_lock(settings.lock(), "cpu");
|
||||||
|
if let Some(cpu) = settings_lock.get_mut(*index as usize) {
|
||||||
|
if let Some(min_max) = &cpu.clock_limits {
|
||||||
|
vec![min_max.max.into(), min_max.min.into()]
|
||||||
|
} else {
|
||||||
|
vec![Primitive::Empty, Primitive::Empty]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec!["get_clock_limits cpu index out of bounds".into()]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec!["get_clock_limits missing parameter 0".into()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unset_clock_limits(
|
||||||
|
settings: Arc<Mutex<Vec<Cpu>>>,
|
||||||
|
saver: Sender<()>,
|
||||||
|
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||||
|
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||||
|
move |params_in: super::ApiParameterType| {
|
||||||
|
if let Some(Primitive::F64(index)) = params_in.get(0) {
|
||||||
|
let mut settings_lock = unwrap_lock(settings.lock(), "cpu");
|
||||||
|
if let Some(cpu) = settings_lock.get_mut(*index as usize) {
|
||||||
|
cpu.clock_limits = None;
|
||||||
|
unwrap_maybe_fatal(
|
||||||
|
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||||
|
"Failed to send on save channel",
|
||||||
|
);
|
||||||
|
vec![]
|
||||||
|
} else {
|
||||||
|
vec!["get_clock_limits cpu index out of bounds".into()]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec!["get_clock_limits missing parameter 0".into()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_cpu_governor(
|
||||||
|
settings: Arc<Mutex<Vec<Cpu>>>,
|
||||||
|
saver: Sender<()>,
|
||||||
|
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||||
|
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||||
|
move |params_in: super::ApiParameterType| {
|
||||||
|
if let Some(Primitive::F64(index)) = params_in.get(0) {
|
||||||
|
let mut settings_lock = unwrap_lock(settings.lock(), "cpu");
|
||||||
|
if let Some(cpu) = settings_lock.get_mut(*index as usize) {
|
||||||
|
if let Some(Primitive::String(governor)) = params_in.get(1) {
|
||||||
|
cpu.governor = governor.to_owned();
|
||||||
|
unwrap_maybe_fatal(
|
||||||
|
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||||
|
"Failed to send on save channel",
|
||||||
|
);
|
||||||
|
super::utility::map_empty_result(
|
||||||
|
cpu.on_set(),
|
||||||
|
&cpu.governor as &str,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
vec!["set_cpu_governor missing parameter 1".into()]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec!["set_cpu_governor cpu index out of bounds".into()]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec!["set_cpu_governor missing parameter 0".into()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_cpu_governors(
|
||||||
|
settings: Arc<Mutex<Vec<Cpu>>>,
|
||||||
|
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||||
|
move |_: super::ApiParameterType| {
|
||||||
|
let settings_lock = unwrap_lock(settings.lock(), "cpu");
|
||||||
|
let mut output = Vec::with_capacity(settings_lock.len());
|
||||||
|
for cpu in settings_lock.as_slice() {
|
||||||
|
output.push(cpu.governor.clone().into());
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
}
|
161
powertools-rs/src/api/gpu.rs
Normal file
161
powertools-rs/src/api/gpu.rs
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
use std::sync::{mpsc::Sender, Arc, Mutex};
|
||||||
|
use usdpl_back::core::serdes::Primitive;
|
||||||
|
|
||||||
|
use crate::settings::{Gpu, OnSet, MinMax};
|
||||||
|
use crate::utility::{unwrap_lock, unwrap_maybe_fatal};
|
||||||
|
|
||||||
|
pub fn set_ppt(
|
||||||
|
settings: Arc<Mutex<Gpu>>,
|
||||||
|
saver: Sender<()>,
|
||||||
|
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||||
|
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||||
|
move |params_in: super::ApiParameterType| {
|
||||||
|
if let Some(Primitive::F64(fast_ppt)) = params_in.get(0) {
|
||||||
|
if let Some(Primitive::F64(slow_ppt)) = params_in.get(1) {
|
||||||
|
let mut settings_lock = unwrap_lock(settings.lock(), "gpu");
|
||||||
|
settings_lock.fast_ppt = Some(*fast_ppt as u64);
|
||||||
|
settings_lock.slow_ppt = Some(*slow_ppt as u64);
|
||||||
|
unwrap_maybe_fatal(
|
||||||
|
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||||
|
"Failed to send on save channel",
|
||||||
|
);
|
||||||
|
match settings_lock.on_set() {
|
||||||
|
Ok(_) => vec![
|
||||||
|
settings_lock.fast_ppt.unwrap().into(),
|
||||||
|
settings_lock.slow_ppt.unwrap().into()
|
||||||
|
],
|
||||||
|
Err(e) => vec![e.msg.into()],
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec!["set_ppt missing parameter 1".into()]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec!["set_ppt missing parameter 0".into()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_ppt(
|
||||||
|
settings: Arc<Mutex<Gpu>>,
|
||||||
|
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||||
|
move |_: super::ApiParameterType| {
|
||||||
|
let settings_lock = unwrap_lock(settings.lock(), "gpu");
|
||||||
|
let fast_ppt = settings_lock.fast_ppt.map(|x| x.into()).unwrap_or(Primitive::Empty);
|
||||||
|
let slow_ppt = settings_lock.slow_ppt.map(|x| x.into()).unwrap_or(Primitive::Empty);
|
||||||
|
vec![fast_ppt, slow_ppt]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unset_ppt(
|
||||||
|
settings: Arc<Mutex<Gpu>>,
|
||||||
|
saver: Sender<()>,
|
||||||
|
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||||
|
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||||
|
move |_: super::ApiParameterType| {
|
||||||
|
let mut settings_lock = unwrap_lock(settings.lock(), "gpu");
|
||||||
|
settings_lock.fast_ppt = None;
|
||||||
|
settings_lock.slow_ppt = None;
|
||||||
|
unwrap_maybe_fatal(
|
||||||
|
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||||
|
"Failed to send on save channel",
|
||||||
|
);
|
||||||
|
super::utility::map_empty_result(
|
||||||
|
settings_lock.on_set(),
|
||||||
|
Primitive::Empty,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_clock_limits(
|
||||||
|
settings: Arc<Mutex<Gpu>>,
|
||||||
|
saver: Sender<()>,
|
||||||
|
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||||
|
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||||
|
move |params_in: super::ApiParameterType| {
|
||||||
|
if let Some(Primitive::F64(min)) = params_in.get(0) {
|
||||||
|
if let Some(Primitive::F64(max)) = params_in.get(1) {
|
||||||
|
let mut settings_lock = unwrap_lock(settings.lock(), "gpu");
|
||||||
|
settings_lock.clock_limits = Some(MinMax {
|
||||||
|
min: *min as _,
|
||||||
|
max: *max as _,
|
||||||
|
});
|
||||||
|
unwrap_maybe_fatal(
|
||||||
|
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||||
|
"Failed to send on save channel",
|
||||||
|
);
|
||||||
|
match settings_lock.on_set() {
|
||||||
|
Ok(_) => vec![
|
||||||
|
settings_lock.clock_limits.as_ref().unwrap().min.into(),
|
||||||
|
settings_lock.clock_limits.as_ref().unwrap().max.into(),
|
||||||
|
],
|
||||||
|
Err(e) => vec![e.msg.into()]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec!["set_clock_limits missing parameter 1".into()]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vec!["set_clock_limits missing parameter 0".into()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_clock_limits(
|
||||||
|
settings: Arc<Mutex<Gpu>>,
|
||||||
|
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||||
|
move |_: super::ApiParameterType| {
|
||||||
|
let settings_lock = unwrap_lock(settings.lock(), "gpu");
|
||||||
|
if let Some(min_max) = &settings_lock.clock_limits {
|
||||||
|
vec![min_max.max.into(), min_max.min.into()]
|
||||||
|
} else {
|
||||||
|
vec![Primitive::Empty, Primitive::Empty]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unset_clock_limits(
|
||||||
|
settings: Arc<Mutex<Gpu>>,
|
||||||
|
saver: Sender<()>,
|
||||||
|
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||||
|
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||||
|
move |_: super::ApiParameterType| {
|
||||||
|
let mut settings_lock = unwrap_lock(settings.lock(), "gpu");
|
||||||
|
settings_lock.clock_limits = None;
|
||||||
|
unwrap_maybe_fatal(
|
||||||
|
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||||
|
"Failed to send on save channel",
|
||||||
|
);
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_slow_memory(
|
||||||
|
settings: Arc<Mutex<Gpu>>,
|
||||||
|
saver: Sender<()>,
|
||||||
|
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||||
|
let saver = Mutex::new(saver); // Sender is not Sync; this is required for safety
|
||||||
|
move |params_in: super::ApiParameterType| {
|
||||||
|
if let Some(Primitive::Bool(memory_is_slow)) = params_in.get(0) {
|
||||||
|
let mut settings_lock = unwrap_lock(settings.lock(), "gpu");
|
||||||
|
settings_lock.slow_memory = *memory_is_slow;
|
||||||
|
unwrap_maybe_fatal(
|
||||||
|
unwrap_lock(saver.lock(), "save channel").send(()),
|
||||||
|
"Failed to send on save channel",
|
||||||
|
);
|
||||||
|
super::utility::map_empty_result(
|
||||||
|
settings_lock.on_set(),
|
||||||
|
settings_lock.slow_memory,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
vec!["set_slow_memory missing parameter 0".into()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_slow_memory(
|
||||||
|
settings: Arc<Mutex<Gpu>>,
|
||||||
|
) -> impl Fn(super::ApiParameterType) -> super::ApiParameterType {
|
||||||
|
move |_: super::ApiParameterType| {
|
||||||
|
let settings_lock = unwrap_lock(settings.lock(), "cpu");
|
||||||
|
vec![settings_lock.slow_memory.into()]
|
||||||
|
}
|
||||||
|
}
|
6
powertools-rs/src/api/mod.rs
Normal file
6
powertools-rs/src/api/mod.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
pub mod battery;
|
||||||
|
pub mod cpu;
|
||||||
|
pub mod gpu;
|
||||||
|
mod utility;
|
||||||
|
|
||||||
|
pub(super) type ApiParameterType = Vec<usdpl_back::core::serdes::Primitive>;
|
21
powertools-rs/src/api/utility.rs
Normal file
21
powertools-rs/src/api/utility.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
use std::convert::Into;
|
||||||
|
use usdpl_back::core::serdes::Primitive;
|
||||||
|
|
||||||
|
use crate::settings::SettingError;
|
||||||
|
|
||||||
|
pub fn map_result<T: Into<Primitive>>(result: Result<T, SettingError>) -> super::ApiParameterType {
|
||||||
|
match result {
|
||||||
|
Ok(val) => vec![val.into()],
|
||||||
|
Err(e) => vec![e.msg.into()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_empty_result<T: Into<Primitive>>(
|
||||||
|
result: Result<(), SettingError>,
|
||||||
|
success: T,
|
||||||
|
) -> super::ApiParameterType {
|
||||||
|
match result {
|
||||||
|
Ok(_) => vec![success.into()],
|
||||||
|
Err(e) => vec![e.msg.into()],
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,35 +1,132 @@
|
||||||
|
mod api;
|
||||||
mod persist;
|
mod persist;
|
||||||
mod settings;
|
mod settings;
|
||||||
|
mod state;
|
||||||
|
|
||||||
use simplelog::{WriteLogger, LevelFilter};
|
mod resume_worker;
|
||||||
|
mod save_worker;
|
||||||
|
mod utility;
|
||||||
|
|
||||||
|
use simplelog::{LevelFilter, WriteLogger};
|
||||||
|
|
||||||
use usdpl_back::Instance;
|
|
||||||
use usdpl_back::core::serdes::Primitive;
|
use usdpl_back::core::serdes::Primitive;
|
||||||
|
use usdpl_back::Instance;
|
||||||
|
|
||||||
const PORT: u16 = 44443;
|
const PORT: u16 = 44443;
|
||||||
|
|
||||||
const PACKAGE_NAME: &'static str = env!("CARGO_PKG_NAME");
|
const PACKAGE_NAME: &'static str = env!("CARGO_PKG_NAME");
|
||||||
const PACKAGE_VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
const PACKAGE_VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
|
const DEFAULT_SETTINGS_FILE: &str = "default_settings.json";
|
||||||
|
|
||||||
fn main() -> Result<(), ()> {
|
fn main() -> Result<(), ()> {
|
||||||
let log_filepath = format!("/tmp/{}.log", PACKAGE_NAME);
|
let log_filepath = format!("/tmp/{}.log", PACKAGE_NAME);
|
||||||
WriteLogger::init(
|
WriteLogger::init(
|
||||||
#[cfg(debug_assertions)]{LevelFilter::Debug},
|
#[cfg(debug_assertions)]
|
||||||
#[cfg(not(debug_assertions))]{LevelFilter::Info},
|
{
|
||||||
|
LevelFilter::Debug
|
||||||
|
},
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
{
|
||||||
|
LevelFilter::Info
|
||||||
|
},
|
||||||
Default::default(),
|
Default::default(),
|
||||||
std::fs::File::create(&log_filepath).unwrap()
|
std::fs::File::create(&log_filepath).unwrap(),
|
||||||
).unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
log::info!("Starting back-end ({} v{})", PACKAGE_NAME, PACKAGE_VERSION);
|
log::info!("Starting back-end ({} v{})", PACKAGE_NAME, PACKAGE_VERSION);
|
||||||
println!("Starting back-end ({} v{})", PACKAGE_NAME, PACKAGE_VERSION);
|
println!("Starting back-end ({} v{})", PACKAGE_NAME, PACKAGE_VERSION);
|
||||||
|
|
||||||
let default_settings: settings::Settings = persist::SettingsJson::open(
|
let default_settings = persist::SettingsJson::open(settings_dir().join(DEFAULT_SETTINGS_FILE))
|
||||||
settings_dir().join("default_settings.json")
|
.map(|settings| settings::Settings::from_json(settings, DEFAULT_SETTINGS_FILE.into()))
|
||||||
).unwrap_or_default().into();
|
.unwrap_or_else(|_| settings::Settings::system_default(DEFAULT_SETTINGS_FILE.into()));
|
||||||
|
|
||||||
log::debug!("Settings: {:?}", default_settings);
|
log::debug!("Settings: {:?}", default_settings);
|
||||||
|
|
||||||
|
let (_save_handle, save_sender) = save_worker::spawn(default_settings.clone());
|
||||||
|
let _resume_handle = resume_worker::spawn(default_settings.clone());
|
||||||
|
|
||||||
Instance::new(PORT)
|
Instance::new(PORT)
|
||||||
.register("hello", |_: Vec<Primitive>| vec![format!("Hello {}", PACKAGE_NAME).into()])
|
.register("hello", |_: Vec<Primitive>| {
|
||||||
|
vec![format!("Hello {}", PACKAGE_NAME).into()]
|
||||||
|
})
|
||||||
|
// battery API functions
|
||||||
|
.register("BATTERY_current_now", api::battery::current_now)
|
||||||
|
.register(
|
||||||
|
"BATTERY_set_charge_rate",
|
||||||
|
api::battery::set_charge_rate(default_settings.battery.clone(), save_sender.clone()),
|
||||||
|
)
|
||||||
|
.register(
|
||||||
|
"BATTERY_get_charge_rate",
|
||||||
|
api::battery::get_charge_rate(default_settings.battery.clone()),
|
||||||
|
)
|
||||||
|
.register(
|
||||||
|
"BATTERY_unset_charge_rate",
|
||||||
|
api::battery::unset_charge_rate(default_settings.battery.clone(), save_sender.clone()),
|
||||||
|
)
|
||||||
|
// cpu API functions
|
||||||
|
.register("CPU_count", api::cpu::max_cpus)
|
||||||
|
.register(
|
||||||
|
"CPU_set_online",
|
||||||
|
api::cpu::set_cpu_online(default_settings.cpus.clone(), save_sender.clone())
|
||||||
|
)
|
||||||
|
.register(
|
||||||
|
"CPU_get_online",
|
||||||
|
api::cpu::get_cpus_online(default_settings.cpus.clone())
|
||||||
|
)
|
||||||
|
.register(
|
||||||
|
"CPU_set_clock_limits",
|
||||||
|
api::cpu::set_clock_limits(default_settings.cpus.clone(), save_sender.clone())
|
||||||
|
)
|
||||||
|
.register(
|
||||||
|
"CPU_get_clock_limits",
|
||||||
|
api::cpu::get_clock_limits(default_settings.cpus.clone())
|
||||||
|
)
|
||||||
|
.register(
|
||||||
|
"CPU_unset_clock_limits",
|
||||||
|
api::cpu::unset_clock_limits(default_settings.cpus.clone(), save_sender.clone())
|
||||||
|
)
|
||||||
|
.register(
|
||||||
|
"CPU_set_governor",
|
||||||
|
api::cpu::set_cpu_governor(default_settings.cpus.clone(), save_sender.clone())
|
||||||
|
)
|
||||||
|
.register(
|
||||||
|
"CPU_get_governors",
|
||||||
|
api::cpu::get_cpu_governors(default_settings.cpus.clone())
|
||||||
|
)
|
||||||
|
// gpu API functions
|
||||||
|
.register(
|
||||||
|
"GPU_set_ppt",
|
||||||
|
api::gpu::set_ppt(default_settings.gpu.clone(), save_sender.clone())
|
||||||
|
)
|
||||||
|
.register(
|
||||||
|
"GPU_get_ppt",
|
||||||
|
api::gpu::get_ppt(default_settings.gpu.clone())
|
||||||
|
)
|
||||||
|
.register(
|
||||||
|
"GPU_unset_ppt",
|
||||||
|
api::gpu::unset_ppt(default_settings.gpu.clone(), save_sender.clone())
|
||||||
|
)
|
||||||
|
.register(
|
||||||
|
"GPU_set_clock_limits",
|
||||||
|
api::gpu::set_clock_limits(default_settings.gpu.clone(), save_sender.clone())
|
||||||
|
)
|
||||||
|
.register(
|
||||||
|
"GPU_get_clock_limits",
|
||||||
|
api::gpu::get_clock_limits(default_settings.gpu.clone())
|
||||||
|
)
|
||||||
|
.register(
|
||||||
|
"GPU_unset_clock_limits",
|
||||||
|
api::gpu::unset_clock_limits(default_settings.gpu.clone(), save_sender.clone())
|
||||||
|
)
|
||||||
|
.register(
|
||||||
|
"GPU_set_slow_memory",
|
||||||
|
api::gpu::set_slow_memory(default_settings.gpu.clone(), save_sender.clone())
|
||||||
|
)
|
||||||
|
.register(
|
||||||
|
"GPU_get_slow_memory",
|
||||||
|
api::gpu::get_slow_memory(default_settings.gpu.clone())
|
||||||
|
)
|
||||||
.run_blocking()
|
.run_blocking()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
powertools-rs/src/persist/battery.rs
Normal file
15
powertools-rs/src/persist/battery.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
use std::default::Default;
|
||||||
|
//use std::fmt::Display;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct BatteryJson {
|
||||||
|
pub charge_rate: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for BatteryJson {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { charge_rate: None }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,27 +1,25 @@
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
//use std::fmt::Display;
|
//use std::fmt::Display;
|
||||||
|
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
const SCALING_FREQUENCIES: &[u64] = &[1700000, 2400000, 2800000];
|
use super::MinMaxJson;
|
||||||
|
|
||||||
|
//const SCALING_FREQUENCIES: &[u64] = &[1700000, 2400000, 2800000];
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct CpuJson {
|
pub struct CpuJson {
|
||||||
pub online: bool,
|
pub online: bool,
|
||||||
pub max_boost: u64,
|
pub clock_limits: Option<MinMaxJson<u64>>,
|
||||||
pub min_boost: u64,
|
|
||||||
pub governor: String,
|
pub governor: String,
|
||||||
pub boost: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CpuJson {
|
impl Default for CpuJson {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
online: true,
|
online: true,
|
||||||
max_boost: SCALING_FREQUENCIES[SCALING_FREQUENCIES.len() - 1],
|
clock_limits: None,
|
||||||
min_boost: SCALING_FREQUENCIES[0],
|
|
||||||
governor: "schedutil".to_owned(),
|
governor: "schedutil".to_owned(),
|
||||||
boost: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
14
powertools-rs/src/persist/error.rs
Normal file
14
powertools-rs/src/persist/error.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum JsonError {
|
||||||
|
Serde(serde_json::Error),
|
||||||
|
Io(std::io::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for JsonError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Serde(e) => (e as &dyn std::fmt::Display).fmt(f),
|
||||||
|
Self::Io(e) => (e as &dyn std::fmt::Display).fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,25 +1,29 @@
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::{CpuJson, GpuJson};
|
use super::JsonError;
|
||||||
|
use super::{BatteryJson, CpuJson, GpuJson};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct SettingsJson {
|
pub struct SettingsJson {
|
||||||
pub version: u64,
|
pub version: u64,
|
||||||
|
pub name: String,
|
||||||
pub persistent: bool,
|
pub persistent: bool,
|
||||||
pub cpus: Vec<CpuJson>,
|
pub cpus: Vec<CpuJson>,
|
||||||
pub gpu: GpuJson,
|
pub gpu: GpuJson,
|
||||||
|
pub battery: BatteryJson,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SettingsJson {
|
impl Default for SettingsJson {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
version: 0,
|
version: 0,
|
||||||
|
name: "default".to_owned(),
|
||||||
persistent: false,
|
persistent: false,
|
||||||
cpus: Vec::with_capacity(8),
|
cpus: Vec::with_capacity(8),
|
||||||
gpu: GpuJson::default(),
|
gpu: GpuJson::default(),
|
||||||
|
battery: BatteryJson::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,17 +44,8 @@ impl SettingsJson {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub enum JsonError {
|
pub struct MinMaxJson<T> {
|
||||||
Serde(serde_json::Error),
|
pub max: T,
|
||||||
Io(std::io::Error),
|
pub min: T,
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for JsonError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Self::Serde(e) => (e as &dyn Display).fmt(f),
|
|
||||||
Self::Io(e) => (e as &dyn Display).fmt(f),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
//use std::fmt::Display;
|
//use std::fmt::Display;
|
||||||
|
|
||||||
use serde::{Serialize, Deserialize};
|
use super::MinMaxJson;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct GpuJson {
|
pub struct GpuJson {
|
||||||
pub fast_ppt: Option<u64>,
|
pub fast_ppt: Option<u64>,
|
||||||
pub slow_ppt: Option<u64>,
|
pub slow_ppt: Option<u64>,
|
||||||
|
pub clock_limits: Option<MinMaxJson<u64>>,
|
||||||
|
pub slow_memory: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for GpuJson {
|
impl Default for GpuJson {
|
||||||
|
@ -14,6 +17,8 @@ impl Default for GpuJson {
|
||||||
Self {
|
Self {
|
||||||
fast_ppt: None,
|
fast_ppt: None,
|
||||||
slow_ppt: None,
|
slow_ppt: None,
|
||||||
|
clock_limits: None,
|
||||||
|
slow_memory: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
|
mod battery;
|
||||||
mod cpu;
|
mod cpu;
|
||||||
|
mod error;
|
||||||
mod general;
|
mod general;
|
||||||
mod gpu;
|
mod gpu;
|
||||||
|
|
||||||
|
pub use battery::BatteryJson;
|
||||||
pub use cpu::CpuJson;
|
pub use cpu::CpuJson;
|
||||||
pub use general::SettingsJson;
|
pub use general::{MinMaxJson, SettingsJson};
|
||||||
pub use gpu::GpuJson;
|
pub use gpu::GpuJson;
|
||||||
|
|
||||||
|
pub use error::JsonError;
|
||||||
|
|
27
powertools-rs/src/resume_worker.rs
Normal file
27
powertools-rs/src/resume_worker.rs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
use std::thread::{self, JoinHandle};
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use crate::settings::{OnResume, Settings};
|
||||||
|
use crate::utility::unwrap_maybe_fatal;
|
||||||
|
|
||||||
|
const ALLOWED_ERROR: f64 = 0.001;
|
||||||
|
|
||||||
|
pub fn spawn(settings: Settings) -> JoinHandle<()> {
|
||||||
|
thread::spawn(move || {
|
||||||
|
let duration = Duration::from_millis(5000);
|
||||||
|
let mut start = Instant::now();
|
||||||
|
loop {
|
||||||
|
thread::sleep(duration);
|
||||||
|
let old_start = start.elapsed();
|
||||||
|
start = Instant::now();
|
||||||
|
if old_start.as_secs_f64() > duration.as_secs_f64() * (1.0 + ALLOWED_ERROR) {
|
||||||
|
// has just resumed from sleep
|
||||||
|
unwrap_maybe_fatal(settings.on_resume(), "On resume failure");
|
||||||
|
log::info!(
|
||||||
|
"OnResume completed after sleeping for {}s",
|
||||||
|
old_start.as_secs_f32()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
19
powertools-rs/src/save_worker.rs
Normal file
19
powertools-rs/src/save_worker.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
use std::sync::mpsc::{self, Receiver, Sender};
|
||||||
|
use std::thread::{self, JoinHandle};
|
||||||
|
|
||||||
|
use crate::persist::SettingsJson;
|
||||||
|
use crate::settings::Settings;
|
||||||
|
use crate::utility::{unwrap_lock, unwrap_maybe_fatal};
|
||||||
|
|
||||||
|
pub fn spawn(settings: Settings) -> (JoinHandle<()>, Sender<()>) {
|
||||||
|
let (sender, receiver): (Sender<()>, Receiver<()>) = mpsc::channel();
|
||||||
|
let worker = thread::spawn(move || {
|
||||||
|
for _ in receiver.iter() {
|
||||||
|
let save_path = unwrap_lock(settings.general.lock(), "general").path.clone();
|
||||||
|
let save_json: SettingsJson = settings.clone().into();
|
||||||
|
unwrap_maybe_fatal(save_json.save(&save_path), "Failed to save settings");
|
||||||
|
log::debug!("Saved settings to {}", save_path.display());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
(worker, sender)
|
||||||
|
}
|
109
powertools-rs/src/settings/battery.rs
Normal file
109
powertools-rs/src/settings/battery.rs
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
use std::convert::Into;
|
||||||
|
|
||||||
|
use super::{OnResume, OnSet, SettingError, SettingsRange};
|
||||||
|
use crate::persist::BatteryJson;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Battery {
|
||||||
|
pub charge_rate: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const BATTERY_CHARGE_RATE_PATH: &str = "/sys/class/hwmon/hwmon5/maximum_battery_charge_rate"; // write-only
|
||||||
|
const BATTERY_CURRENT_NOW_PATH: &str = "/sys/class/power_supply/BAT1/current_now"; // read-only
|
||||||
|
|
||||||
|
impl Battery {
|
||||||
|
#[inline]
|
||||||
|
pub fn from_json(other: BatteryJson, version: u64) -> Self {
|
||||||
|
match version {
|
||||||
|
0 => Self {
|
||||||
|
charge_rate: other.charge_rate,
|
||||||
|
},
|
||||||
|
_ => Self {
|
||||||
|
charge_rate: other.charge_rate,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_all(&self) -> Result<(), SettingError> {
|
||||||
|
if let Some(charge_rate) = self.charge_rate {
|
||||||
|
usdpl_back::api::files::write_single(BATTERY_CHARGE_RATE_PATH, charge_rate).map_err(
|
||||||
|
|e| SettingError {
|
||||||
|
msg: format!("Failed to write to `{}`: {}", BATTERY_CHARGE_RATE_PATH, e),
|
||||||
|
setting: super::SettingVariant::Battery,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clamp_all(&mut self) {
|
||||||
|
let min = Self::min();
|
||||||
|
let max = Self::max();
|
||||||
|
if let Some(charge_rate) = &mut self.charge_rate {
|
||||||
|
*charge_rate = (*charge_rate).clamp(min.charge_rate.unwrap(), max.charge_rate.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current_now() -> Result<u64, SettingError> {
|
||||||
|
match usdpl_back::api::files::read_single::<_, u64, _>(BATTERY_CURRENT_NOW_PATH) {
|
||||||
|
Err((Some(e), None)) => Err(SettingError {
|
||||||
|
msg: format!("Failed to read from `{}`: {}", BATTERY_CURRENT_NOW_PATH, e),
|
||||||
|
setting: super::SettingVariant::Battery,
|
||||||
|
}),
|
||||||
|
Err((None, Some(e))) => Err(SettingError {
|
||||||
|
msg: format!("Failed to read from `{}`: {}", BATTERY_CURRENT_NOW_PATH, e),
|
||||||
|
setting: super::SettingVariant::Battery,
|
||||||
|
}),
|
||||||
|
Err(_) => panic!(
|
||||||
|
"Invalid error while reading from `{}`",
|
||||||
|
BATTERY_CURRENT_NOW_PATH
|
||||||
|
),
|
||||||
|
// this value is in uA, while it's set in mA
|
||||||
|
// so convert this to mA for consistency
|
||||||
|
Ok(val) => Ok(val / 1000),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn system_default() -> Self {
|
||||||
|
Self { charge_rate: None }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<BatteryJson> for Battery {
|
||||||
|
#[inline]
|
||||||
|
fn into(self) -> BatteryJson {
|
||||||
|
BatteryJson {
|
||||||
|
charge_rate: self.charge_rate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnSet for Battery {
|
||||||
|
fn on_set(&mut self) -> Result<(), SettingError> {
|
||||||
|
self.clamp_all();
|
||||||
|
self.set_all()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnResume for Battery {
|
||||||
|
fn on_resume(&self) -> Result<(), SettingError> {
|
||||||
|
self.set_all()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SettingsRange for Battery {
|
||||||
|
#[inline]
|
||||||
|
fn max() -> Self {
|
||||||
|
Self {
|
||||||
|
charge_rate: Some(2500),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn min() -> Self {
|
||||||
|
Self {
|
||||||
|
charge_rate: Some(250),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,34 +1,162 @@
|
||||||
use std::convert::Into;
|
use std::convert::Into;
|
||||||
|
|
||||||
|
use super::MinMax;
|
||||||
|
use super::{OnResume, OnSet, SettingError, SettingsRange};
|
||||||
use crate::persist::CpuJson;
|
use crate::persist::CpuJson;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Cpu {
|
pub struct Cpu {
|
||||||
pub online: bool,
|
pub online: bool,
|
||||||
pub max_boost: u64,
|
pub clock_limits: Option<MinMax<u64>>,
|
||||||
pub min_boost: u64,
|
|
||||||
pub governor: String,
|
pub governor: String,
|
||||||
pub boost: bool,
|
index: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage";
|
||||||
|
const CPU_FORCE_LIMITS_PATH: &str = "/sys/class/drm/card0/device/power_dpm_force_performance_level";
|
||||||
|
|
||||||
|
const CPU_PRESENT_PATH: &str = "/sys/devices/system/cpu/present";
|
||||||
|
|
||||||
impl Cpu {
|
impl Cpu {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_json(other: CpuJson, version: u64) -> Self {
|
pub fn from_json(other: CpuJson, version: u64, i: usize) -> Self {
|
||||||
match version {
|
match version {
|
||||||
0 => Self {
|
0 => Self {
|
||||||
online: other.online,
|
online: other.online,
|
||||||
max_boost: other.max_boost,
|
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||||
min_boost: other.min_boost,
|
|
||||||
governor: other.governor,
|
governor: other.governor,
|
||||||
boost: other.boost,
|
index: i,
|
||||||
},
|
},
|
||||||
_ => Self {
|
_ => Self {
|
||||||
online: other.online,
|
online: other.online,
|
||||||
max_boost: other.max_boost,
|
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||||
min_boost: other.min_boost,
|
|
||||||
governor: other.governor,
|
governor: other.governor,
|
||||||
boost: other.boost,
|
index: i,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_all(&self) -> Result<(), SettingError> {
|
||||||
|
// set cpu online/offline
|
||||||
|
if self.index != 0 { // cpu0 cannot be disabled
|
||||||
|
let online_path = cpu_online_path(self.index);
|
||||||
|
usdpl_back::api::files::write_single(&online_path, self.online as u8).map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!("Failed to write to `{}`: {}", &online_path, e),
|
||||||
|
setting: super::SettingVariant::Cpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
// set clock limits
|
||||||
|
if let Some(clock_limits) = &self.clock_limits {
|
||||||
|
// set manual control
|
||||||
|
usdpl_back::api::files::write_single(CPU_FORCE_LIMITS_PATH, "manual").map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `manual` to `{}`: {}",
|
||||||
|
CPU_FORCE_LIMITS_PATH, e
|
||||||
|
),
|
||||||
|
setting: super::SettingVariant::Cpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
// max clock
|
||||||
|
let payload_max = format!("p {} 1 {}", self.index / 2, clock_limits.max);
|
||||||
|
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_max).map_err(
|
||||||
|
|e| SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `{}` to `{}`: {}",
|
||||||
|
&payload_max, CPU_CLOCK_LIMITS_PATH, e
|
||||||
|
),
|
||||||
|
setting: super::SettingVariant::Cpu,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
// min clock
|
||||||
|
let payload_min = format!("p {} 0 {}", self.index / 2, clock_limits.min);
|
||||||
|
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_min).map_err(
|
||||||
|
|e| SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `{}` to `{}`: {}",
|
||||||
|
&payload_min, CPU_CLOCK_LIMITS_PATH, e
|
||||||
|
),
|
||||||
|
setting: super::SettingVariant::Cpu,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
// commit changes
|
||||||
|
usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, "c").map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!("Failed to write `c` to `{}`: {}", CPU_CLOCK_LIMITS_PATH, e),
|
||||||
|
setting: super::SettingVariant::Cpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
} else {
|
||||||
|
// disable manual clock limits
|
||||||
|
usdpl_back::api::files::write_single(CPU_FORCE_LIMITS_PATH, "auto").map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `auto` to `{}`: {}",
|
||||||
|
CPU_FORCE_LIMITS_PATH, e
|
||||||
|
),
|
||||||
|
setting: super::SettingVariant::Cpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
// set governor
|
||||||
|
let governor_path = cpu_governor_path(self.index);
|
||||||
|
usdpl_back::api::files::write_single(&governor_path, &self.governor).map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `{}` to `{}`: {}",
|
||||||
|
&self.governor, &governor_path, e
|
||||||
|
),
|
||||||
|
setting: super::SettingVariant::Cpu,
|
||||||
}
|
}
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clamp_all(&mut self) {
|
||||||
|
let min = Self::min();
|
||||||
|
let max = Self::max();
|
||||||
|
if let Some(clock_limits) = &mut self.clock_limits {
|
||||||
|
let max_boost = max.clock_limits.as_ref().unwrap();
|
||||||
|
let min_boost = min.clock_limits.as_ref().unwrap();
|
||||||
|
clock_limits.min = clock_limits.min.clamp(min_boost.min, max_boost.min);
|
||||||
|
clock_limits.max = clock_limits.max.clamp(min_boost.max, max_boost.max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_sys(index: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
online: usdpl_back::api::files::read_single(cpu_online_path(index)).unwrap_or(1u8) != 0,
|
||||||
|
clock_limits: None,
|
||||||
|
governor: usdpl_back::api::files::read_single(cpu_governor_path(index))
|
||||||
|
.unwrap_or("schedutil".to_owned()),
|
||||||
|
index: index,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cpu_count() -> Option<usize> {
|
||||||
|
let mut data: String = usdpl_back::api::files::read_single(CPU_PRESENT_PATH)
|
||||||
|
.unwrap_or_else(|_| "0-7".to_string() /* Steam Deck's default */);
|
||||||
|
if let Some(dash_index) = data.find('-') {
|
||||||
|
let data = data.split_off(dash_index + 1);
|
||||||
|
if let Ok(max_cpu) = data.parse::<usize>() {
|
||||||
|
return Some(max_cpu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log::warn!("Failed to parse CPU info from kernel, is Tux evil?");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn system_default() -> Vec<Self> {
|
||||||
|
if let Some(max_cpu) = Self::cpu_count() {
|
||||||
|
let mut cpus = Vec::with_capacity(max_cpu + 1);
|
||||||
|
for i in 0..=max_cpu {
|
||||||
|
cpus.push(Self::from_sys(i));
|
||||||
|
}
|
||||||
|
cpus
|
||||||
|
} else {
|
||||||
|
Vec::with_capacity(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,10 +166,59 @@ impl Into<CpuJson> for Cpu {
|
||||||
fn into(self) -> CpuJson {
|
fn into(self) -> CpuJson {
|
||||||
CpuJson {
|
CpuJson {
|
||||||
online: self.online,
|
online: self.online,
|
||||||
max_boost: self.max_boost,
|
clock_limits: self.clock_limits.map(|x| x.into()),
|
||||||
min_boost: self.min_boost,
|
|
||||||
governor: self.governor,
|
governor: self.governor,
|
||||||
boost: self.boost,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl OnSet for Cpu {
|
||||||
|
fn on_set(&mut self) -> Result<(), SettingError> {
|
||||||
|
self.clamp_all();
|
||||||
|
self.set_all()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnResume for Cpu {
|
||||||
|
fn on_resume(&self) -> Result<(), SettingError> {
|
||||||
|
self.set_all()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SettingsRange for Cpu {
|
||||||
|
#[inline]
|
||||||
|
fn max() -> Self {
|
||||||
|
Self {
|
||||||
|
online: true,
|
||||||
|
clock_limits: Some(MinMax {
|
||||||
|
max: 3500,
|
||||||
|
min: 3500,
|
||||||
|
}),
|
||||||
|
governor: "schedutil".to_owned(),
|
||||||
|
index: usize::MAX,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn min() -> Self {
|
||||||
|
Self {
|
||||||
|
online: false,
|
||||||
|
clock_limits: Some(MinMax { max: 500, min: 1400 }),
|
||||||
|
governor: "schedutil".to_owned(),
|
||||||
|
index: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn cpu_online_path(index: usize) -> String {
|
||||||
|
format!("/sys/devices/system/cpu/cpu{}/online", index)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn cpu_governor_path(index: usize) -> String {
|
||||||
|
format!(
|
||||||
|
"/sys/devices/system/cpu/cpu{}/cpufreq/scaling_governor",
|
||||||
|
index
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
13
powertools-rs/src/settings/error.rs
Normal file
13
powertools-rs/src/settings/error.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
use super::SettingVariant;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SettingError {
|
||||||
|
pub msg: String,
|
||||||
|
pub setting: SettingVariant,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for SettingError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "{} setting error: {}", self.setting, self.msg)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,43 +1,150 @@
|
||||||
use std::convert::{Into, From};
|
use std::convert::Into;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use crate::persist::SettingsJson;
|
use super::{Battery, Cpu, Gpu};
|
||||||
use super::{Cpu, Gpu};
|
use super::{OnResume, OnSet, SettingError};
|
||||||
|
use crate::persist::{CpuJson, SettingsJson};
|
||||||
|
use crate::utility::unwrap_lock;
|
||||||
|
|
||||||
const LATEST_VERSION: u64 = 0;
|
const LATEST_VERSION: u64 = 0;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Settings {
|
pub enum SettingVariant {
|
||||||
pub persistent: bool,
|
Battery,
|
||||||
pub cpus: Vec<Cpu>,
|
Cpu,
|
||||||
pub gpu: Gpu,
|
Gpu,
|
||||||
|
General,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SettingsJson> for Settings {
|
impl std::fmt::Display for SettingVariant {
|
||||||
#[inline]
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
fn from(mut other: SettingsJson) -> Self {
|
match self {
|
||||||
match other.version {
|
Self::Battery => write!(f, "Battery"),
|
||||||
0 => Self {
|
Self::Cpu => write!(f, "CPU"),
|
||||||
persistent: other.persistent,
|
Self::Gpu => write!(f, "GPU"),
|
||||||
cpus: other.cpus.drain(..).map(|cpu| Cpu::from_json(cpu, other.version)).collect(),
|
Self::General => write!(f, "General"),
|
||||||
gpu: Gpu::from_json(other.gpu, other.version),
|
}
|
||||||
},
|
}
|
||||||
_ => Self {
|
}
|
||||||
persistent: other.persistent,
|
|
||||||
cpus: other.cpus.drain(..).map(|cpu| Cpu::from_json(cpu, other.version)).collect(),
|
#[derive(Debug, Clone)]
|
||||||
gpu: Gpu::from_json(other.gpu, other.version),
|
pub struct General {
|
||||||
|
pub persistent: bool,
|
||||||
|
pub path: PathBuf,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Settings {
|
||||||
|
pub general: Arc<Mutex<General>>,
|
||||||
|
pub cpus: Arc<Mutex<Vec<Cpu>>>,
|
||||||
|
pub gpu: Arc<Mutex<Gpu>>,
|
||||||
|
pub battery: Arc<Mutex<Battery>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnSet for Settings {
|
||||||
|
fn on_set(&mut self) -> Result<(), SettingError> {
|
||||||
|
unwrap_lock(self.battery.lock(), "battery").on_set()?;
|
||||||
|
{
|
||||||
|
// cpu lock scope
|
||||||
|
let mut cpu_lock = unwrap_lock(self.cpus.lock(), "cpu");
|
||||||
|
for cpu in cpu_lock.iter_mut() {
|
||||||
|
cpu.on_set()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
unwrap_lock(self.gpu.lock(), "gpu").on_set()?;
|
||||||
|
{
|
||||||
|
// general lock scope
|
||||||
|
let gen_lock = unwrap_lock(self.general.lock(), "general");
|
||||||
|
if !gen_lock.persistent && gen_lock.path.exists() {
|
||||||
|
std::fs::remove_file(&gen_lock.path).map_err(|e| SettingError {
|
||||||
|
msg: format!("Failed to delete `{}`: {}", gen_lock.path.display(), e),
|
||||||
|
setting: SettingVariant::General,
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Settings {
|
||||||
|
#[inline]
|
||||||
|
pub fn from_json(other: SettingsJson, json_path: PathBuf) -> Self {
|
||||||
|
match other.version {
|
||||||
|
0 => Self {
|
||||||
|
general: Arc::new(Mutex::new(General {
|
||||||
|
persistent: other.persistent,
|
||||||
|
path: json_path,
|
||||||
|
name: other.name,
|
||||||
|
})),
|
||||||
|
cpus: Arc::new(Mutex::new(Self::convert_cpus(other.cpus, other.version))),
|
||||||
|
gpu: Arc::new(Mutex::new(Gpu::from_json(other.gpu, other.version))),
|
||||||
|
battery: Arc::new(Mutex::new(Battery::from_json(other.battery, other.version))),
|
||||||
|
},
|
||||||
|
_ => Self {
|
||||||
|
general: Arc::new(Mutex::new(General {
|
||||||
|
persistent: other.persistent,
|
||||||
|
path: json_path,
|
||||||
|
name: other.name,
|
||||||
|
})),
|
||||||
|
cpus: Arc::new(Mutex::new(Self::convert_cpus(other.cpus, other.version))),
|
||||||
|
gpu: Arc::new(Mutex::new(Gpu::from_json(other.gpu, other.version))),
|
||||||
|
battery: Arc::new(Mutex::new(Battery::from_json(other.battery, other.version))),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_cpus(mut cpus: Vec<CpuJson>, version: u64) -> Vec<Cpu> {
|
||||||
|
let mut result = Vec::with_capacity(cpus.len());
|
||||||
|
for (i, cpu) in cpus.drain(..).enumerate() {
|
||||||
|
result.push(Cpu::from_json(cpu, version, i));
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn system_default(json_path: PathBuf) -> Self {
|
||||||
|
Self {
|
||||||
|
general: Arc::new(Mutex::new(General {
|
||||||
|
persistent: false,
|
||||||
|
path: json_path,
|
||||||
|
name: "".to_owned(),
|
||||||
|
})),
|
||||||
|
cpus: Arc::new(Mutex::new(Cpu::system_default())),
|
||||||
|
gpu: Arc::new(Mutex::new(Gpu::system_default())),
|
||||||
|
battery: Arc::new(Mutex::new(Battery::system_default())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnResume for Settings {
|
||||||
|
fn on_resume(&self) -> Result<(), SettingError> {
|
||||||
|
unwrap_lock(self.battery.lock(), "battery").on_resume()?;
|
||||||
|
{
|
||||||
|
let mut cpu_lock = unwrap_lock(self.cpus.lock(), "cpu");
|
||||||
|
for cpu in cpu_lock.iter_mut() {
|
||||||
|
cpu.on_resume()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unwrap_lock(self.gpu.lock(), "gpu").on_resume()?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<SettingsJson> for Settings {
|
impl Into<SettingsJson> for Settings {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into(mut self) -> SettingsJson {
|
fn into(self) -> SettingsJson {
|
||||||
SettingsJson {
|
SettingsJson {
|
||||||
version: LATEST_VERSION,
|
version: LATEST_VERSION,
|
||||||
persistent: self.persistent,
|
name: unwrap_lock(self.general.lock(), "general").name.clone(),
|
||||||
cpus: self.cpus.drain(..).map(|cpu| cpu.into()).collect(),
|
persistent: unwrap_lock(self.general.lock(), "general").persistent,
|
||||||
gpu: self.gpu.into()
|
cpus: unwrap_lock(self.cpus.lock(), "cpu")
|
||||||
|
.clone()
|
||||||
|
.drain(..)
|
||||||
|
.map(|cpu| cpu.into())
|
||||||
|
.collect(),
|
||||||
|
gpu: unwrap_lock(self.gpu.lock(), "gpu").clone().into(),
|
||||||
|
battery: unwrap_lock(self.battery.lock(), "battery").clone().into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,25 @@
|
||||||
use std::convert::Into;
|
use std::convert::Into;
|
||||||
|
|
||||||
|
use super::MinMax;
|
||||||
|
use super::{OnResume, OnSet, SettingError, SettingsRange};
|
||||||
use crate::persist::GpuJson;
|
use crate::persist::GpuJson;
|
||||||
|
|
||||||
|
const SLOW_PPT: u8 = 1;
|
||||||
|
const FAST_PPT: u8 = 2;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Gpu {
|
pub struct Gpu {
|
||||||
pub fast_ppt: Option<u64>,
|
pub fast_ppt: Option<u64>,
|
||||||
pub slow_ppt: Option<u64>,
|
pub slow_ppt: Option<u64>,
|
||||||
|
pub clock_limits: Option<MinMax<u64>>,
|
||||||
|
pub slow_memory: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// same as CPU
|
||||||
|
const GPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage";
|
||||||
|
const GPU_FORCE_LIMITS_PATH: &str = "/sys/class/drm/card0/device/power_dpm_force_performance_level";
|
||||||
|
const GPU_MEMORY_DOWNCLOCK_PATH: &str = "/sys/class/drm/card0/device/pp_dpm_fclk";
|
||||||
|
|
||||||
impl Gpu {
|
impl Gpu {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_json(other: GpuJson, version: u64) -> Self {
|
pub fn from_json(other: GpuJson, version: u64) -> Self {
|
||||||
|
@ -15,11 +27,140 @@ impl Gpu {
|
||||||
0 => Self {
|
0 => Self {
|
||||||
fast_ppt: other.fast_ppt,
|
fast_ppt: other.fast_ppt,
|
||||||
slow_ppt: other.slow_ppt,
|
slow_ppt: other.slow_ppt,
|
||||||
|
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||||
|
slow_memory: other.slow_memory,
|
||||||
},
|
},
|
||||||
_ => Self {
|
_ => Self {
|
||||||
fast_ppt: other.fast_ppt,
|
fast_ppt: other.fast_ppt,
|
||||||
slow_ppt: other.slow_ppt,
|
slow_ppt: other.slow_ppt,
|
||||||
|
clock_limits: other.clock_limits.map(|x| MinMax::from_json(x, version)),
|
||||||
|
slow_memory: other.slow_memory,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_all(&self) -> Result<(), SettingError> {
|
||||||
|
// set fast PPT
|
||||||
|
if let Some(fast_ppt) = &self.fast_ppt {
|
||||||
|
let fast_ppt_path = gpu_power_path(FAST_PPT);
|
||||||
|
usdpl_back::api::files::write_single(&fast_ppt_path, fast_ppt).map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `{}` to `{}`: {}",
|
||||||
|
fast_ppt, &fast_ppt_path, e
|
||||||
|
),
|
||||||
|
setting: super::SettingVariant::Gpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
// set slow PPT
|
||||||
|
if let Some(slow_ppt) = &self.slow_ppt {
|
||||||
|
let slow_ppt_path = gpu_power_path(SLOW_PPT);
|
||||||
|
usdpl_back::api::files::write_single(&slow_ppt_path, slow_ppt).map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `{}` to `{}`: {}",
|
||||||
|
slow_ppt, &slow_ppt_path, e
|
||||||
|
),
|
||||||
|
setting: super::SettingVariant::Gpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
// settings using force_performance_level
|
||||||
|
if self.clock_limits.is_some() || self.slow_memory {
|
||||||
|
// set manual control
|
||||||
|
usdpl_back::api::files::write_single(GPU_FORCE_LIMITS_PATH, "manual").map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `manual` to `{}`: {}",
|
||||||
|
GPU_FORCE_LIMITS_PATH, e
|
||||||
|
),
|
||||||
|
setting: super::SettingVariant::Gpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
// set clock limits
|
||||||
|
if let Some(clock_limits) = &self.clock_limits {
|
||||||
|
// max clock
|
||||||
|
let payload_max = format!("s 1 {}", clock_limits.max);
|
||||||
|
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_max).map_err(
|
||||||
|
|e| SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `{}` to `{}`: {}",
|
||||||
|
&payload_max, GPU_CLOCK_LIMITS_PATH, e
|
||||||
|
),
|
||||||
|
setting: super::SettingVariant::Gpu,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
// min clock
|
||||||
|
let payload_min = format!("s 0 {}", clock_limits.min);
|
||||||
|
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_min).map_err(
|
||||||
|
|e| SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `{}` to `{}`: {}",
|
||||||
|
&payload_min, GPU_CLOCK_LIMITS_PATH, e
|
||||||
|
),
|
||||||
|
setting: super::SettingVariant::Gpu,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
|
// force downclock of GPU memory (to 400Mhz?)
|
||||||
|
usdpl_back::api::files::write_single(GPU_MEMORY_DOWNCLOCK_PATH, self.slow_memory as u8)
|
||||||
|
.map_err(|e| SettingError {
|
||||||
|
msg: format!("Failed to write to `{}`: {}", GPU_MEMORY_DOWNCLOCK_PATH, e),
|
||||||
|
setting: super::SettingVariant::Gpu,
|
||||||
|
})?;
|
||||||
|
// commit changes
|
||||||
|
usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, "c").map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!("Failed to write `c` to `{}`: {}", GPU_CLOCK_LIMITS_PATH, e),
|
||||||
|
setting: super::SettingVariant::Gpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
} else {
|
||||||
|
// disable manual clock limits
|
||||||
|
usdpl_back::api::files::write_single(GPU_FORCE_LIMITS_PATH, "auto").map_err(|e| {
|
||||||
|
SettingError {
|
||||||
|
msg: format!(
|
||||||
|
"Failed to write `auto` to `{}`: {}",
|
||||||
|
GPU_FORCE_LIMITS_PATH, e
|
||||||
|
),
|
||||||
|
setting: super::SettingVariant::Gpu,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(()) // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clamp_all(&mut self) {
|
||||||
|
let min = Self::min();
|
||||||
|
let max = Self::max();
|
||||||
|
if let Some(fast_ppt) = &mut self.fast_ppt {
|
||||||
|
*fast_ppt = (*fast_ppt).clamp(
|
||||||
|
*min.fast_ppt.as_ref().unwrap(),
|
||||||
|
*max.fast_ppt.as_ref().unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(slow_ppt) = &mut self.slow_ppt {
|
||||||
|
*slow_ppt = (*slow_ppt).clamp(
|
||||||
|
*min.slow_ppt.as_ref().unwrap(),
|
||||||
|
*max.slow_ppt.as_ref().unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(clock_limits) = &mut self.clock_limits {
|
||||||
|
let max_boost = max.clock_limits.as_ref().unwrap();
|
||||||
|
let min_boost = min.clock_limits.as_ref().unwrap();
|
||||||
|
clock_limits.min = clock_limits.min.clamp(min_boost.min, max_boost.min);
|
||||||
|
clock_limits.max = clock_limits.max.clamp(min_boost.max, max_boost.max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn system_default() -> Self {
|
||||||
|
Self {
|
||||||
|
fast_ppt: None,
|
||||||
|
slow_ppt: None,
|
||||||
|
clock_limits: None,
|
||||||
|
slow_memory: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +171,51 @@ impl Into<GpuJson> for Gpu {
|
||||||
GpuJson {
|
GpuJson {
|
||||||
fast_ppt: self.fast_ppt,
|
fast_ppt: self.fast_ppt,
|
||||||
slow_ppt: self.slow_ppt,
|
slow_ppt: self.slow_ppt,
|
||||||
|
clock_limits: self.clock_limits.map(|x| x.into()),
|
||||||
|
slow_memory: self.slow_memory,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl OnSet for Gpu {
|
||||||
|
fn on_set(&mut self) -> Result<(), SettingError> {
|
||||||
|
self.clamp_all();
|
||||||
|
self.set_all()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OnResume for Gpu {
|
||||||
|
fn on_resume(&self) -> Result<(), SettingError> {
|
||||||
|
self.set_all()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SettingsRange for Gpu {
|
||||||
|
#[inline]
|
||||||
|
fn max() -> Self {
|
||||||
|
Self {
|
||||||
|
fast_ppt: Some(30000000),
|
||||||
|
slow_ppt: Some(29000000),
|
||||||
|
clock_limits: Some(MinMax {
|
||||||
|
min: 1600,
|
||||||
|
max: 1600,
|
||||||
|
}),
|
||||||
|
slow_memory: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn min() -> Self {
|
||||||
|
Self {
|
||||||
|
fast_ppt: Some(0),
|
||||||
|
slow_ppt: Some(1000000),
|
||||||
|
clock_limits: Some(MinMax { min: 200, max: 200 }),
|
||||||
|
slow_memory: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn gpu_power_path(power_number: u8) -> String {
|
||||||
|
format!("/sys/class/hwmon/hwmon4/power{}_cap", power_number)
|
||||||
|
}
|
||||||
|
|
29
powertools-rs/src/settings/min_max.rs
Normal file
29
powertools-rs/src/settings/min_max.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
use std::convert::Into;
|
||||||
|
|
||||||
|
use crate::persist::MinMaxJson;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct MinMax<T> {
|
||||||
|
pub max: T,
|
||||||
|
pub min: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> MinMax<T> {
|
||||||
|
#[inline]
|
||||||
|
pub fn from_json<X: Into<T>>(other: MinMaxJson<X>, _version: u64) -> Self {
|
||||||
|
Self {
|
||||||
|
max: other.max.into(),
|
||||||
|
min: other.min.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<X: Into<Y>, Y> Into<MinMaxJson<Y>> for MinMax<X> {
|
||||||
|
#[inline]
|
||||||
|
fn into(self) -> MinMaxJson<Y> {
|
||||||
|
MinMaxJson {
|
||||||
|
max: self.max.into(),
|
||||||
|
min: self.min.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,25 @@
|
||||||
|
mod battery;
|
||||||
mod cpu;
|
mod cpu;
|
||||||
|
mod error;
|
||||||
mod general;
|
mod general;
|
||||||
mod gpu;
|
mod gpu;
|
||||||
|
mod min_max;
|
||||||
|
mod traits;
|
||||||
|
|
||||||
|
pub use battery::Battery;
|
||||||
pub use cpu::Cpu;
|
pub use cpu::Cpu;
|
||||||
pub use general::Settings;
|
pub use general::{SettingVariant, Settings};
|
||||||
pub use gpu::Gpu;
|
pub use gpu::Gpu;
|
||||||
|
pub use min_max::MinMax;
|
||||||
|
|
||||||
|
pub use error::SettingError;
|
||||||
|
pub use traits::{OnResume, OnSet, SettingsRange};
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
#[test]
|
||||||
|
fn system_defaults_test() {
|
||||||
|
let settings = super::Settings::system_default("idc".into());
|
||||||
|
println!("Loaded system settings: {:?}", settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
14
powertools-rs/src/settings/traits.rs
Normal file
14
powertools-rs/src/settings/traits.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
use super::SettingError;
|
||||||
|
|
||||||
|
pub trait OnSet {
|
||||||
|
fn on_set(&mut self) -> Result<(), SettingError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait OnResume {
|
||||||
|
fn on_resume(&self) -> Result<(), SettingError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SettingsRange {
|
||||||
|
fn max() -> Self;
|
||||||
|
fn min() -> Self;
|
||||||
|
}
|
16
powertools-rs/src/state/error.rs
Normal file
16
powertools-rs/src/state/error.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
use crate::settings::SettingVariant;
|
||||||
|
|
||||||
|
pub struct StateError {
|
||||||
|
pub msg: String,
|
||||||
|
pub setting: Option<SettingVariant>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for StateError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
if let Some(setting) = self.setting {
|
||||||
|
write!(f, "{} setting state error: {}", setting, self.msg)
|
||||||
|
} else {
|
||||||
|
write!(f, "State error: {}", self.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
powertools-rs/src/state/mod.rs
Normal file
5
powertools-rs/src/state/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
mod error;
|
||||||
|
mod traits;
|
||||||
|
|
||||||
|
pub use error::StateError;
|
||||||
|
pub use traits::OnPoll;
|
5
powertools-rs/src/state/traits.rs
Normal file
5
powertools-rs/src/state/traits.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
use super::StateError;
|
||||||
|
|
||||||
|
pub trait OnPoll {
|
||||||
|
fn on_poll(&self) -> Result<(), StateError>;
|
||||||
|
}
|
25
powertools-rs/src/utility.rs
Normal file
25
powertools-rs/src/utility.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::sync::{LockResult, MutexGuard};
|
||||||
|
|
||||||
|
pub fn unwrap_maybe_fatal<T: Sized, E: Display>(result: Result<T, E>, message: &str) -> T {
|
||||||
|
match result {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("{}: {}", message, e);
|
||||||
|
panic!("{}: {}", message, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unwrap_lock<'a, T: Sized>(
|
||||||
|
result: LockResult<MutexGuard<'a, T>>,
|
||||||
|
lock_name: &str,
|
||||||
|
) -> MutexGuard<'a, T> {
|
||||||
|
match result {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to acquire {} lock: {}", lock_name, e);
|
||||||
|
panic!("Failed to acquire {} lock: {}", lock_name, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue