NetDesk 1.5.0 — source release

This commit is contained in:
NetDesk committed 2026-10-18 01:35:59 +02:00
commit ec4955682f
227 files changed
+80669

No files matched your search

+118
View File
@@ -0,0 +1,118 @@
//! Lightweight host metrics (CPU %, RAM, disk, battery, uptime) for the portal's
//! live monitoring. Windows-only; sampled every few seconds by the engine and
//! pushed to the server as a `host.telemetry` control message.
#![cfg(windows)]
use serde_json::{json, Value};
/// Keeps the previous CPU time sample so each `sample()` yields a real % over
/// the interval since the last call.
pub struct Telemetry {
prev_idle: u64,
prev_total: u64,
}
impl Telemetry {
pub fn new() -> Self {
Self { prev_idle: 0, prev_total: 0 }
}
pub fn sample(&mut self) -> Value {
let cpu = self.cpu_percent();
let (ram_used, ram_total) = mem();
let (disk_used, disk_total) = disk();
let (battery, charging) = power();
json!({
"t": "host.telemetry",
"cpu": cpu,
"ram_used": ram_used, "ram_total": ram_total,
"disk_used": disk_used, "disk_total": disk_total,
"battery": battery, "charging": charging,
"uptime": uptime_secs(),
})
}
fn cpu_percent(&mut self) -> u32 {
use windows::Win32::Foundation::FILETIME;
use windows::Win32::System::Threading::GetSystemTimes;
unsafe {
let mut idle = FILETIME::default();
let mut kern = FILETIME::default();
let mut user = FILETIME::default();
if GetSystemTimes(Some(&mut idle), Some(&mut kern), Some(&mut user)).is_err() {
return 0;
}
let ft = |f: FILETIME| ((f.dwHighDateTime as u64) << 32) | f.dwLowDateTime as u64;
let idle_t = ft(idle);
// Kernel time INCLUDES idle, so kernel+user is the full busy+idle total.
let total_t = ft(kern) + ft(user);
let d_total = total_t.saturating_sub(self.prev_total);
let d_idle = idle_t.saturating_sub(self.prev_idle);
self.prev_total = total_t;
self.prev_idle = idle_t;
if d_total == 0 {
return 0;
}
let busy = d_total.saturating_sub(d_idle);
((busy as f64 / d_total as f64) * 100.0).round().clamp(0.0, 100.0) as u32
}
}
}
fn mem() -> (u64, u64) {
use windows::Win32::System::SystemInformation::{GlobalMemoryStatusEx, MEMORYSTATUSEX};
unsafe {
let mut m = MEMORYSTATUSEX {
dwLength: std::mem::size_of::<MEMORYSTATUSEX>() as u32,
..Default::default()
};
if GlobalMemoryStatusEx(&mut m).is_ok() {
(m.ullTotalPhys.saturating_sub(m.ullAvailPhys), m.ullTotalPhys)
} else {
(0, 0)
}
}
}
fn disk() -> (u64, u64) {
use windows::core::PCWSTR;
use windows::Win32::Storage::FileSystem::GetDiskFreeSpaceExW;
unsafe {
let path: Vec<u16> = "C:\\".encode_utf16().chain(std::iter::once(0)).collect();
let mut free_avail = 0u64;
let mut total = 0u64;
let mut total_free = 0u64;
if GetDiskFreeSpaceExW(
PCWSTR(path.as_ptr()),
Some(&mut free_avail),
Some(&mut total),
Some(&mut total_free),
)
.is_ok()
{
(total.saturating_sub(total_free), total)
} else {
(0, 0)
}
}
}
fn power() -> (i32, bool) {
use windows::Win32::System::Power::GetSystemPowerStatus;
use windows::Win32::System::Power::SYSTEM_POWER_STATUS;
unsafe {
let mut s = SYSTEM_POWER_STATUS::default();
if GetSystemPowerStatus(&mut s).is_ok() {
let pct = if s.BatteryLifePercent == 255 { -1 } else { s.BatteryLifePercent as i32 };
let charging = s.ACLineStatus == 1; // 1 = plugged in
(pct, charging)
} else {
(-1, true)
}
}
}
fn uptime_secs() -> u64 {
use windows::Win32::System::SystemInformation::GetTickCount64;
unsafe { GetTickCount64() / 1000 }
}