#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use crate::{
api_test::{HardwareType, Sensor, SensorType},
wmic::HardwareMonitor,
};
use csv::StringRecord;
use e_utils::AnyResult;
use std::fs::File;
use std::path::Path;
use std::{fs, io::BufReader};
use std::{
io::{self},
path::PathBuf,
};
#[derive(Debug, Default)]
pub struct ArgusMonitor {
pub temperatures: Vec<f64>, pub frequencys: Vec<f64>, pub loads: Vec<f64>, pub powers: Vec<f64>, }
impl ArgusMonitor {
pub fn read_log_path() -> AnyResult<PathBuf> {
let mut latest_log = None;
let entries = fs::read_dir(Self::DIR)?;
for entry in entries {
if let Ok(entry) = entry {
let path = entry.path();
if path.is_file() {
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
if name.starts_with("ArgusMonitor_CSVLog") && name.ends_with(".csv") {
let metadata = fs::metadata(&path)?;
let modified = metadata.modified()?;
if latest_log.as_ref().map_or(true, |(t, _)| modified > *t) {
latest_log = Some((modified, path));
}
}
}
}
}
}
if let Some((_, path)) = latest_log {
Ok(path)
} else {
Err(io::Error::new(io::ErrorKind::NotFound, "No ArgusMonitor_CSVLog.csv file found").into())
}
}
}
impl ArgusMonitor {
pub fn parse_log<P: AsRef<Path>>(path: P) -> e_utils::AnyResult<Vec<StringRecord>> {
let file = File::open(path)?;
let mut rdr = csv::ReaderBuilder::new()
.flexible(true)
.has_headers(true)
.delimiter(b',')
.from_reader(BufReader::new(file));
let mut datas = vec![];
for result in rdr.records() {
match result {
Ok(record) => {
datas.push(record);
}
Err(_) => {
}
}
}
Ok(datas)
}
}
impl ArgusMonitor {
pub fn from_csv(row: csv::StringRecord) -> Self {
let num_cores = (0..)
.take_while(|&i| row.get(i + 1).and_then(|s| s.parse::<f64>().ok()).map_or(false, |t| t > 0.0 && t < 150.0))
.count();
let temperatures: Vec<f64> = (0..num_cores)
.filter_map(|i| row.get(i + 1))
.filter_map(|s| s.parse::<f64>().ok())
.filter(|&t| t > 0.0 && t < 150.0)
.collect();
let loads: Vec<f64> = (0..num_cores)
.filter_map(|i| row.get(6 + i * 5))
.filter_map(|s| s.parse().ok())
.filter(|&l| l >= 0.0 && l <= 100.0)
.collect();
let freqs: Vec<f64> = (0..num_cores)
.filter_map(|i| row.get(7 + i * 5)) .filter_map(|s| s.parse().ok())
.filter(|&f| f >= 500.0) .collect();
let power = vec![row
.get(row.len().saturating_sub(2))
.and_then(|s| s.parse().ok())
.filter(|&p| p >= 0.0 && p <= 500.0)
.unwrap_or_default()];
Self {
temperatures,
frequencys: freqs,
loads: loads,
powers: power,
}
}
}
impl ArgusMonitor {
pub const EXE: &'static str = "ArgusMonitor.exe";
pub const DIR: &'static str = "plugins/ArgusMonitor";
fn parse_value(value: Vec<f64>) -> (f64, f64, f64) {
let temperature = value.iter().sum::<f64>() / value.len() as f64;
let min = value.iter().min_by(|a, b| a.partial_cmp(b).unwrap()).cloned().unwrap_or(0.0);
let max = value.iter().max_by(|a, b| a.partial_cmp(b).unwrap()).cloned().unwrap_or(0.0);
(temperature, min, max)
}
pub fn query(&self, hw_type: HardwareType, stype: SensorType) -> AnyResult<Vec<Sensor>> {
let path = Self::read_log_path()?;
let records = Self::parse_log(path)?;
let mut sensors = vec![];
let mut i = 0;
for record in records {
i += 1;
let target = match hw_type {
HardwareType::ALL | HardwareType::CPU => match stype {
SensorType::Temperature => {
let core_temp = Self::from_csv(record);
core_temp.temperatures
}
SensorType::Voltage => {
let core_temp = Self::from_csv(record);
core_temp.powers
}
SensorType::Clock => {
let core_temp = Self::from_csv(record);
core_temp.frequencys
}
SensorType::Load => {
let core_temp = Self::from_csv(record);
core_temp.loads
}
SensorType::Power => {
let core_temp = Self::from_csv(record);
core_temp.powers
}
SensorType::ALL => {
let core_temp = Self::from_csv(record);
if core_temp.temperatures.len() > 0 {
core_temp.temperatures
} else if core_temp.powers.len() > 0 {
core_temp.powers
} else if core_temp.loads.len() > 0 {
core_temp.loads
} else if core_temp.frequencys.len() > 0 {
core_temp.frequencys
} else {
vec![]
}
}
_ => return Err(format!("ArgusMonitor {} {} not supported", hw_type, stype).into()),
},
_ => return Err(format!("ArgusMonitor {} not supported", hw_type).into()),
};
let (value, min, max) = Self::parse_value(target);
let sensor = Sensor {
Name: format!("ArgusMonitor"),
Identifier: format!("ArgusMonitor"),
_SensorType: stype.to_string(),
SensorType: stype.clone(),
Parent: hw_type.to_string(),
Value: value,
Min: min,
Max: max,
Index: i,
data: String::new(),
};
sensors.push(sensor);
}
Ok(sensors)
}
}
impl HardwareMonitor for ArgusMonitor {
type HWType = HardwareType;
type SensorType = Sensor;
const CON_QUERY: &'static str = "";
const HW_QUERY: &'static str = "";
const SENSOR_QUERY: &'static str = "";
fn new() -> AnyResult<Self> {
Ok(Self::default())
}
fn test(count: u64) -> AnyResult<()> {
for i in 1..=count {
match Self::new() {
Ok(api) => {
let has_value = [(HardwareType::CPU, SensorType::Clock), (HardwareType::ALL, SensorType::Temperature)]
.into_iter()
.any(|(hw_type, sensor_type)| {
api
.query(hw_type, sensor_type)
.ok()
.and_then(|v| v.first().cloned())
.map(|v| v.Value != 0.0)
.unwrap_or(false)
});
if has_value {
crate::dp(format!("Loading... ({}%/{}%)", count, count));
crate::dp("ArgusMonitor ready");
return Ok(());
}
}
Err(e) => crate::wp(e.to_string()),
}
crate::dp(format!("Loading... ({}%/{}%)", i, count));
std::thread::sleep(std::time::Duration::from_millis(200));
}
Err("ArgusMonitor load timeout".into())
}
fn stop() -> AnyResult<()> {
Ok(())
}
fn clean() -> AnyResult<()> {
let entries = fs::read_dir(Self::DIR)?;
for entry in entries {
if let Ok(entry) = entry {
let path = entry.path();
if path.is_file() {
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
if name.starts_with("ArgusMonitor_CSVLog") && name.ends_with(".csv") {
fs::remove_file(path).ok();
}
}
}
}
}
Ok(())
}
}