Files
pyMC_Repeater/repeater/data_acquisition/hardware_stats.py

174 lines
5.9 KiB
Python

"""
Hardware statistics collection using psutil.
KISS - Keep It Simple Stupid approach.
"""
try:
import psutil
PSUTIL_AVAILABLE = True
except ImportError:
PSUTIL_AVAILABLE = False
psutil = None
import time
import logging
logger = logging.getLogger("HardwareStats")
class HardwareStatsCollector:
def __init__(self):
self.start_time = time.time()
def get_stats(self):
if not PSUTIL_AVAILABLE:
logger.error("psutil not available - cannot collect hardware stats")
return {
"error": "psutil library not available - cannot collect hardware statistics"
}
try:
# Get current timestamp
now = time.time()
uptime = now - self.start_time
# CPU stats
cpu_percent = psutil.cpu_percent(interval=0.1)
cpu_count = psutil.cpu_count()
cpu_freq = psutil.cpu_freq()
# Memory stats
memory = psutil.virtual_memory()
# Disk stats
disk = psutil.disk_usage('/')
# Network stats (total across all interfaces)
net_io = psutil.net_io_counters()
# Load average (Unix only)
load_avg = None
try:
load_avg = psutil.getloadavg()
except (AttributeError, OSError):
# Not available on all systems - use zeros
load_avg = (0.0, 0.0, 0.0)
# System boot time
boot_time = psutil.boot_time()
system_uptime = now - boot_time
# Temperature (if available)
temperatures = {}
try:
temps = psutil.sensors_temperatures()
for name, entries in temps.items():
for i, entry in enumerate(entries):
temp_name = f"{name}_{i}" if len(entries) > 1 else name
temperatures[temp_name] = entry.current
except (AttributeError, OSError):
# Temperature sensors not available
pass
# Format data structure to match Vue component expectations
stats = {
"cpu": {
"usage_percent": cpu_percent,
"count": cpu_count,
"frequency": cpu_freq.current if cpu_freq else 0,
"load_avg": {
"1min": load_avg[0],
"5min": load_avg[1],
"15min": load_avg[2]
}
},
"memory": {
"total": memory.total,
"available": memory.available,
"used": memory.used,
"usage_percent": memory.percent
},
"disk": {
"total": disk.total,
"used": disk.used,
"free": disk.free,
"usage_percent": round((disk.used / disk.total) * 100, 1)
},
"network": {
"bytes_sent": net_io.bytes_sent,
"bytes_recv": net_io.bytes_recv,
"packets_sent": net_io.packets_sent,
"packets_recv": net_io.packets_recv
},
"system": {
"uptime": system_uptime,
"boot_time": boot_time
}
}
# Add temperatures if available
if temperatures:
stats["temperatures"] = temperatures
return stats
except Exception as e:
logger.error(f"Error collecting hardware stats: {e}")
return {
"error": str(e)
}
def get_processes_summary(self, limit=10):
"""
Get top processes by CPU and memory usage.
Returns a dictionary with process information in the format expected by the UI.
"""
if not PSUTIL_AVAILABLE:
logger.error("psutil not available - cannot collect process stats")
return {
"processes": [],
"total_processes": 0,
"error": "psutil library not available - cannot collect process statistics"
}
try:
processes = []
# Get all processes
for proc in psutil.process_iter(['pid', 'name', 'cpu_percent', 'memory_percent', 'memory_info']):
try:
pinfo = proc.info
# Calculate memory in MB
memory_mb = 0
if pinfo['memory_info']:
memory_mb = pinfo['memory_info'].rss / 1024 / 1024 # RSS in MB
process_data = {
"pid": pinfo['pid'],
"name": pinfo['name'] or 'Unknown',
"cpu_percent": pinfo['cpu_percent'] or 0.0,
"memory_percent": pinfo['memory_percent'] or 0.0,
"memory_mb": round(memory_mb, 1)
}
processes.append(process_data)
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
# Sort by CPU usage and get top processes
top_processes = sorted(processes, key=lambda x: x['cpu_percent'], reverse=True)[:limit]
return {
"processes": top_processes,
"total_processes": len(processes)
}
except Exception as e:
logger.error(f"Error collecting process stats: {e}")
return {
"processes": [],
"total_processes": 0,
"error": str(e)
}