implemented multiple fans

This commit is contained in:
2026-02-26 14:06:49 +01:00
parent 7e2bef58d2
commit cab39a6478
10 changed files with 101 additions and 44 deletions

View File

@@ -10,19 +10,20 @@ use tracing::debug;
pub struct DellXps9380Sal {
temp_path: PathBuf,
pwr_path: PathBuf,
fan_path: PathBuf,
fan_paths: Vec<PathBuf>,
freq_path: PathBuf,
pl1_path: PathBuf,
pl2_path: PathBuf,
last_poll: Mutex<Instant>,
last_temp: Mutex<f32>,
last_fan: Mutex<u32>,
last_fans: Mutex<Vec<u32>>,
}
impl DellXps9380Sal {
pub fn init() -> Result<Self> {
let mut temp_path = None;
let mut pwr_path = None;
let mut fan_path = None;
let mut fan_paths = Vec::new();
let mut rapl_base_path = None;
// Dynamic hwmon discovery
@@ -33,7 +34,17 @@ impl DellXps9380Sal {
if name == "dell_smm" {
temp_path = Some(p.join("temp1_input"));
fan_path = Some(p.join("fan1_input"));
// Discover all fans
if let Ok(fan_entries) = fs::read_dir(&p) {
for fan_entry in fan_entries.flatten() {
let fan_p = fan_entry.path();
if fan_p.file_name().unwrap_or_default().to_string_lossy().starts_with("fan") &&
fan_p.file_name().unwrap_or_default().to_string_lossy().ends_with("_input") {
fan_paths.push(fan_p);
}
}
}
fan_paths.sort();
}
if name == "intel_rapl" || name == "rapl" {
@@ -59,16 +70,18 @@ impl DellXps9380Sal {
}
let rapl_base = rapl_base_path.context("Could not find RAPL package-0 path in powercap")?;
let freq_path = PathBuf::from("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq");
Ok(Self {
temp_path: temp_path.context("Could not find dell_smm temperature path")?,
pwr_path: pwr_path.context("Could not find RAPL power path")?,
fan_path: fan_path.context("Could not find dell_smm fan path")?,
fan_paths,
freq_path,
pl1_path: rapl_base.join("constraint_0_power_limit_uw"),
pl2_path: rapl_base.join("constraint_1_power_limit_uw"),
last_poll: Mutex::new(Instant::now() - Duration::from_secs(2)),
last_temp: Mutex::new(0.0),
last_fan: Mutex::new(0),
last_fans: Mutex::new(Vec::new()),
})
}
}
@@ -179,20 +192,32 @@ impl SensorBus for DellXps9380Sal {
}
}
fn get_fan_rpm(&self) -> Result<u32> {
fn get_fan_rpms(&self) -> Result<Vec<u32>> {
let mut last_poll = self.last_poll.lock().unwrap();
let now = Instant::now();
if now.duration_since(*last_poll) < Duration::from_millis(1000) {
return Ok(*self.last_fan.lock().unwrap());
return Ok(self.last_fans.lock().unwrap().clone());
}
let s = fs::read_to_string(&self.fan_path)?;
let val = s.trim().parse::<u32>()?;
let mut fans = Vec::new();
for path in &self.fan_paths {
if let Ok(s) = fs::read_to_string(path) {
if let Ok(rpm) = s.trim().parse::<u32>() {
fans.push(rpm);
}
}
}
*self.last_fan.lock().unwrap() = val;
*self.last_fans.lock().unwrap() = fans.clone();
*last_poll = now;
Ok(fans)
}
fn get_freq_mhz(&self) -> Result<f32> {
let s = fs::read_to_string(&self.freq_path)?;
let val = s.trim().parse::<f32>()? / 1000.0;
Ok(val)
}
}

View File

@@ -50,8 +50,11 @@ impl SensorBus for MockSensorBus {
fn get_power_w(&self) -> Result<f32> {
Ok(15.0)
}
fn get_fan_rpm(&self) -> Result<u32> {
Ok(2500)
fn get_fan_rpms(&self) -> Result<Vec<u32>> {
Ok(vec![2500])
}
fn get_freq_mhz(&self) -> Result<f32> {
Ok(3200.0)
}
}

View File

@@ -54,10 +54,11 @@ pub trait EnvironmentGuard {
}
/// Read-only interface for standardized metrics.
pub trait SensorBus {
pub trait SensorBus: Send + Sync {
fn get_temp(&self) -> Result<f32>;
fn get_power_w(&self) -> Result<f32>;
fn get_fan_rpm(&self) -> Result<u32>;
fn get_fan_rpms(&self) -> Result<Vec<u32>>;
fn get_freq_mhz(&self) -> Result<f32>;
}
impl<T: SensorBus + ?Sized> SensorBus for Arc<T> {
@@ -67,8 +68,11 @@ impl<T: SensorBus + ?Sized> SensorBus for Arc<T> {
fn get_power_w(&self) -> Result<f32> {
(**self).get_power_w()
}
fn get_fan_rpm(&self) -> Result<u32> {
(**self).get_fan_rpm()
fn get_fan_rpms(&self) -> Result<Vec<u32>> {
(**self).get_fan_rpms()
}
fn get_freq_mhz(&self) -> Result<f32> {
(**self).get_freq_mhz()
}
}