use std::ffi::{c_void, CString};
use std::ptr::NonNull;
#[repr(C)]
#[derive(Debug)]
pub struct PowerHandle(*mut c_void);
#[repr(C)]
#[derive(Debug)]
pub enum SensorType {
Unknown = 0,
I2C = 1,
System = 2,
}
#[repr(C)]
#[derive(Debug)]
pub struct SensorData {
pub name: [u8; 64],
pub type_: SensorType,
pub voltage: f64,
pub current: f64,
pub power: f64,
pub online: bool,
pub status: [u8; 32],
pub warning_threshold: f64,
pub critical_threshold: f64,
}
#[repr(C)]
#[derive(Debug)]
pub struct Stats {
pub min: f64,
pub max: f64,
pub avg: f64,
pub total: f64,
pub count: u64,
}
#[repr(C)]
#[derive(Debug)]
pub struct SensorStats {
pub name: [u8; 64],
pub voltage: Stats,
pub current: Stats,
pub power: Stats,
}
#[repr(C)]
#[derive(Debug)]
pub struct PowerData {
pub total: SensorData,
pub sensors: *const SensorData,
pub sensor_count: i32,
}
#[repr(C)]
#[derive(Debug)]
pub struct PowerStats {
pub total: SensorStats,
pub sensors: *const SensorStats,
pub sensor_count: i32,
}
#[derive(Debug)]
#[repr(i32)]
pub enum Error {
InitFailed = -1,
NotInitialized = -2,
AlreadyRunning = -3,
NotRunning = -4,
InvalidFrequency = -5,
NoSensors = -6,
FileAccess = -7,
Memory = -8,
Thread = -9,
Unknown(i32) = -10,
}
impl From<i32> for Error {
fn from(code: i32) -> Self {
match code {
-1 => Error::InitFailed,
-2 => Error::NotInitialized,
-3 => Error::AlreadyRunning,
-4 => Error::NotRunning,
-5 => Error::InvalidFrequency,
-6 => Error::NoSensors,
-7 => Error::FileAccess,
-8 => Error::Memory,
-9 => Error::Thread,
_ => Error::Unknown(code),
}
}
}
impl From<Error> for i32 {
fn from(error: Error) -> Self {
match error {
Error::InitFailed => -1,
Error::NotInitialized => -2,
Error::AlreadyRunning => -3,
Error::NotRunning => -4,
Error::InvalidFrequency => -5,
Error::NoSensors => -6,
Error::FileAccess => -7,
Error::Memory => -8,
Error::Thread => -9,
Error::Unknown(code) => code,
}
}
}
pub struct PowerMonitor {
handle: NonNull<c_void>,
}
impl PowerMonitor {
pub fn new() -> Result<Self, Error> {
let mut handle = std::ptr::null_mut();
let result = unsafe { pm_init(&mut handle) };
if result != 0 {
return Err(result.into());
}
Ok(Self {
handle: NonNull::new(handle).unwrap(),
})
}
pub fn set_sampling_frequency(&self, frequency_hz: i32) -> Result<(), Error> {
let result = unsafe { pm_set_sampling_frequency(self.handle.as_ptr(), frequency_hz) };
if result != 0 {
return Err(result.into());
}
Ok(())
}
pub fn get_sampling_frequency(&self) -> Result<i32, Error> {
let mut frequency = 0;
let result = unsafe { pm_get_sampling_frequency(self.handle.as_ptr(), &mut frequency) };
if result != 0 {
return Err(result.into());
}
Ok(frequency)
}
pub fn start_sampling(&self) -> Result<(), Error> {
let result = unsafe { pm_start_sampling(self.handle.as_ptr()) };
if result != 0 {
return Err(result.into());
}
Ok(())
}
pub fn stop_sampling(&self) -> Result<(), Error> {
let result = unsafe { pm_stop_sampling(self.handle.as_ptr()) };
if result != 0 {
return Err(result.into());
}
Ok(())
}
pub fn is_sampling(&self) -> Result<bool, Error> {
let mut is_sampling = false;
let result = unsafe { pm_is_sampling(self.handle.as_ptr(), &mut is_sampling) };
if result != 0 {
return Err(result.into());
}
Ok(is_sampling)
}
pub fn get_latest_data(&self) -> Result<PowerData, Error> {
let mut data = PowerData {
total: unsafe { std::mem::zeroed() },
sensors: std::ptr::null(),
sensor_count: 0,
};
let result = unsafe { pm_get_latest_data(self.handle.as_ptr(), &mut data) };
if result != 0 {
return Err(result.into());
}
Ok(data)
}
pub fn get_statistics(&self) -> Result<PowerStats, Error> {
let mut stats = PowerStats {
total: unsafe { std::mem::zeroed() },
sensors: std::ptr::null(),
sensor_count: 0,
};
let result = unsafe { pm_get_statistics(self.handle.as_ptr(), &mut stats) };
if result != 0 {
return Err(result.into());
}
Ok(stats)
}
pub fn reset_statistics(&self) -> Result<(), Error> {
let result = unsafe { pm_reset_statistics(self.handle.as_ptr()) };
if result != 0 {
return Err(result.into());
}
Ok(())
}
pub fn get_sensor_count(&self) -> Result<i32, Error> {
let mut count = 0;
let result = unsafe { pm_get_sensor_count(self.handle.as_ptr(), &mut count) };
if result != 0 {
return Err(result.into());
}
Ok(count)
}
#[deprecated(
since = "1.1.0",
note = "This function is unsafe and will be removed in a future version. Please use get_latest_data() or get_statistics() instead."
)]
pub fn get_sensor_names(&self) -> Result<Vec<String>, Error> {
let count = self.get_sensor_count()?;
let mut names = vec![std::ptr::null_mut(); count as usize];
for i in 0..count as usize {
names[i] = unsafe { std::alloc::alloc(std::alloc::Layout::array::<i8>(64).unwrap()) as *mut i8 };
if names[i].is_null() {
for j in 0..i {
unsafe { std::alloc::dealloc(names[j] as *mut u8, std::alloc::Layout::array::<i8>(64).unwrap()) };
}
return Err(Error::Memory);
}
}
let mut count = count;
let result = unsafe {
pm_get_sensor_names(
self.handle.as_ptr(),
names.as_mut_ptr(),
&mut count,
)
};
if result != 0 {
for ptr in names.iter() {
if !ptr.is_null() {
unsafe { std::alloc::dealloc(*ptr as *mut u8, std::alloc::Layout::array::<i8>(64).unwrap()) };
}
}
return Err(result.into());
}
let mut result = Vec::with_capacity(count as usize);
for ptr in names.into_iter().take(count as usize) {
if !ptr.is_null() {
unsafe {
let cstr = CString::from_raw(ptr as *mut _);
result.push(cstr.into_string().unwrap_or_default());
}
}
}
Ok(result)
}
}
impl Drop for PowerMonitor {
fn drop(&mut self) {
unsafe {
pm_cleanup(self.handle.as_ptr());
}
}
}
extern "C" {
fn pm_init(handle: *mut *mut c_void) -> i32;
fn pm_cleanup(handle: *mut c_void) -> i32;
fn pm_set_sampling_frequency(handle: *mut c_void, frequency_hz: i32) -> i32;
fn pm_get_sampling_frequency(handle: *mut c_void, frequency_hz: *mut i32) -> i32;
fn pm_start_sampling(handle: *mut c_void) -> i32;
fn pm_stop_sampling(handle: *mut c_void) -> i32;
fn pm_is_sampling(handle: *mut c_void, is_sampling: *mut bool) -> i32;
fn pm_get_latest_data(handle: *mut c_void, data: *mut PowerData) -> i32;
fn pm_get_statistics(handle: *mut c_void, stats: *mut PowerStats) -> i32;
fn pm_reset_statistics(handle: *mut c_void) -> i32;
fn pm_get_sensor_count(handle: *mut c_void, count: *mut i32) -> i32;
fn pm_get_sensor_names(handle: *mut c_void, names: *mut *mut i8, count: *mut i32) -> i32;
}