video_monitor/app/utils/OSSystem.py

428 lines
16 KiB
Python
Raw Normal View History

2026-08-30 22:22:11 +08:00
import psutil
import shutil
from datetime import datetime
import platform
import subprocess
import os
class OSSystem():
def __init__(self):
self.__system_name = platform.system() # 操作系统
self.__machine_node = str(platform.node()) # 机器名称
@staticmethod
def getDateFmtStr(spend_date, spend_date_fmt="%d%d小时%d分钟%d"): # type <class 'datetime.timedelta'>
spend_day = spend_date.days # 已运行的天数 int
spend_seconds = spend_date.seconds # 已运行的秒数 int
spend_hour = int(spend_seconds / 60 / 60) # 已运行小时 int
spend_seconds -= spend_hour * 60 * 60 # 已运行的秒数 int
spend_minute = int(spend_seconds / 60) # 已运行的分钟 int
spend_seconds -= spend_minute * 60 # 已运行的秒数 int
spend_date_str = spend_date_fmt % (spend_day, spend_hour, spend_minute, spend_seconds)
return spend_date_str
def __byteFormat(self, bytes, suffix="B"):
"""
Scale bytes to its proper format
e.g:
1253656 => '1.20MB'
1253656678 => '1.17GB'
"""
factor = 1024
for unit in ["", "K", "M", "G", "T", "P"]:
if bytes < factor:
return f"{bytes:.2f}{unit}{suffix}"
bytes /= factor
def getOSInfo(self, spend_date_fmt="%d%d小时%d分钟%d", include_gpu=False):
# 获取系统cpu比例 start
os_cpu_used = psutil.cpu_percent()
# os_cpu_physical_core = psutil.cpu_count(logical=False) # 物理核心数量
os_cpu_total_core = psutil.cpu_count(logical=True) # 逻辑核心数量
os_cpu_used_rate = round(os_cpu_used / 100, 3) # <class 'float'> 0.125
# 获取系统cpu比例 end
# 获取系统内存比例 start
os_virtual_mem = psutil.virtual_memory()
os_virtual_mem_total = os_virtual_mem.total
if os_virtual_mem.total == 0:
os_virtual_mem_used_rate = 0
else:
os_virtual_mem_used_rate = os_virtual_mem.used / os_virtual_mem.total
os_virtual_mem_used_rate = round(os_virtual_mem_used_rate, 3) # <class 'float'> 0.635
# 获取系统内存比例 end
# 获取系统磁盘比例 start
os_disk_total = 0
os_disk_used = 0
os_disk_free = 0
os_disk_partitions = psutil.disk_partitions()
for partition in os_disk_partitions:
try:
partition_usage = psutil.disk_usage(partition.mountpoint)
os_disk_total += partition_usage.total
os_disk_free += partition_usage.free
os_disk_used += partition_usage.used
except Exception as e:
pass
if os_disk_total == 0:
os_disk_used_rate = 0
else:
os_disk_used_rate = os_disk_used / os_disk_total
os_disk_used_rate = round(os_disk_used_rate, 3) # 当前系统磁盘占用比例
# 获取系统磁盘比例 end
# 获取系统开机时间 start
os_boot_timestamp = int(psutil.boot_time()) # <class 'float'> 1651904713.9067075
os_boot_date = datetime.fromtimestamp(os_boot_timestamp) # <class 'datetime.datetime'>
os_run_date = datetime.now() - os_boot_date # <class 'datetime.timedelta'>
os_run_date_str = self.getDateFmtStr(os_run_date, spend_date_fmt=spend_date_fmt)
# 获取系统开机时间 end
os_gpus = []
if include_gpu:
try:
from app.utils.GpuInfo import get_gpu_info
os_gpus = get_gpu_info()
except Exception:
os_gpus = []
os_info = {
"machine_node": str(platform.node()),
"system_name": self.getSystemName(),
"os_cpu_used_rate": os_cpu_used_rate, # cpu总占比
"os_virtual_mem_used_rate": os_virtual_mem_used_rate, # 内存总占比
"os_disk_used_rate": os_disk_used_rate,
"os_cpu_used_rate_str": str(round(os_cpu_used_rate * 100, 1)) + "% / " + str(os_cpu_total_core),
"os_virtual_mem_used_rate_str": str(round(os_virtual_mem_used_rate * 100, 1)) + "% / " + str(
self.__byteFormat(os_virtual_mem_total)),
"os_disk_used_rate_str": str(round(os_disk_used_rate * 100, 1)) + "% / " + str(
self.__byteFormat(os_disk_total)),
"os_run_date_str": os_run_date_str,
"os_gpus": os_gpus,
}
return os_info
def getSystemName(self):
return self.__system_name
def getMachineNode(self):
return self.__machine_node
def getMachineOsRelease(self):
# cat /etc/os-release
if self.getSystemName() == "Windows":
__str = "Windows"
else:
try:
result = os.popen("cat /etc/os-release")
__str = str(result.read()).strip()
except:
__str = "run error"
return __str
def getMachineLsCpu(self):
# lscpu
if self.getSystemName() == "Windows":
__str = "Windows"
else:
try:
result = os.popen("lscpu")
__str = str(result.read()).strip()
except:
__str = "run error"
return __str
def getMachineUnameA(self):
# uname -a
if self.getSystemName() == "Windows":
__str = "Windows"
else:
try:
result = os.popen("uname -a")
__str = str(result.read()).strip()
except:
__str = "run error"
return __str
def getMachineCpu(self):
"""
获取系统 CPU 信息
兼容 Windows 11 (PowerShell) Linux 系统
采用多种方法确保可靠获取
"""
system_name = self.getSystemName()
if system_name == "Windows":
machine_cpu = None
# --- 方案一PowerShell Get-CimInstance (推荐Win8+ 兼容) ---
try:
ps_command = [
"powershell",
"-Command",
"Get-CimInstance -ClassName Win32_Processor | Select-Object -ExpandProperty Name"
]
output = subprocess.check_output(
ps_command,
stderr=subprocess.STDOUT,
timeout=10,
encoding='utf-8',
creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0
)
machine_cpu = output.strip()
# 验证结果是否有效
if machine_cpu and len(machine_cpu) > 3 and not machine_cpu.startswith("Get-CimInstance"):
return machine_cpu
except Exception as e:
pass
# --- 方案二WMIC 命令 (旧版 Windows 回退方案) ---
try:
output = subprocess.check_output(
"wmic cpu get Name",
shell=True,
timeout=10,
encoding='utf-8',
creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0
)
lines = [line.strip() for line in output.splitlines() if line.strip()]
# 解析输出,跳过标题行 "Name"
if len(lines) >= 2:
# 第一行是 "Name",第二行开始是 CPU 名称
machine_cpu = lines[1].strip()
if machine_cpu and len(machine_cpu) > 3:
return machine_cpu
except Exception as e:
pass
# --- 方案三PowerShell Get-WmiObject (备选,兼容旧系统) ---
try:
ps_command = [
"powershell",
"-Command",
"Get-WmiObject -Class Win32_Processor | Select-Object -ExpandProperty Name"
]
output = subprocess.check_output(
ps_command,
stderr=subprocess.STDOUT,
timeout=10,
encoding='utf-8',
creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, 'CREATE_NO_WINDOW') else 0
)
machine_cpu = output.strip()
if machine_cpu and len(machine_cpu) > 3 and not machine_cpu.startswith("Get-WmiObject"):
return machine_cpu
except Exception as e:
pass
# --- 方案四:使用 psutil (如果可用) ---
try:
cpu_info = platform.processor()
if cpu_info and len(cpu_info) > 3:
return cpu_info
except Exception:
pass
return "run error"
else: # Linux / macOS / 其他
# --- 方案一:直接读取 /proc/cpuinfo (最可靠,不需要外部命令) ---
try:
with open('/proc/cpuinfo', 'r', encoding='utf-8') as f:
for line in f:
if line.startswith("model name"):
machine_cpu = line.split(":", 1)[1].strip()
if machine_cpu:
return machine_cpu
except Exception as e:
pass
# --- 方案二:使用 lscpu 命令 ---
try:
result = subprocess.run(
['lscpu'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
timeout=10,
universal_newlines=True
)
if result.returncode == 0:
for line in result.stdout.splitlines():
if line.startswith("Model name"):
machine_cpu = line.split(":", 1)[1].strip()
if machine_cpu:
return machine_cpu
except Exception as e:
pass
# --- 方案三:使用 uname -m 获取架构信息 ---
try:
result = subprocess.run(
['uname', '-m'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
timeout=10,
universal_newlines=True
)
if result.returncode == 0:
arch = result.stdout.strip()
if arch:
# 对于 ARM 设备,返回架构信息
return f"ARM Processor ({arch})"
except Exception as e:
pass
# --- 方案四:尝试读取设备树信息 (适用于嵌入式 ARM 设备) ---
try:
dt_paths = [
'/proc/device-tree/model',
'/proc/device-tree/compatible'
]
for path in dt_paths:
if os.path.exists(path):
with open(path, 'rb') as f:
value = f.read().rstrip(b'\x00').decode('utf-8', errors='ignore')
if value and len(value) > 3:
return value.replace('\n', ' ').strip()
except Exception as e:
pass
return "run error"
def getMachineNvidia(self):
# nvidia-smi
try:
if shutil.which("nvidia-smi"):
result = os.popen("nvidia-smi")
__str = str(result.read()).strip()
if len(__str) > 0:
lines = __str.split("\n")
lines_filter = []
for line in lines:
if line.find("NVIDIA-SMI") > -1:
lines_filter.append(line.strip())
elif line.find("Default") > -1:
lines_filter.append(line.strip())
elif line.find("NVIDIA") > -1:
lines_filter.append(line.strip())
__str = ",".join(lines_filter)
else:
__str = ""
else:
__str = "no"
except:
__str = "run error"
return __str
def getMachineAscend(self):
# npu-smi info
if self.getSystemName() == "Windows":
return ""
else:
# 2. 定义可能的 npu-smi 路径 (防止 PATH 未配置)
# 昇腾默认安装路径通常在 /usr/local/Ascend/driver/tools/
possible_paths = [
"npu-smi",
"/usr/local/Ascend/driver/tools/npu-smi",
"/usr/local/Ascend/bin/npu-smi"
]
cmd_path = None
# 优先查找环境变量中的命令,如果找不到则尝试硬编码路径
for path in possible_paths:
if shutil.which(path):
cmd_path = path
break
if not cmd_path:
return "no_cmd_found" # 明确区分是找不到命令还是执行失败
try:
# 3. 使用 subprocess.run 替代 os.popen
# capture_output=True: 同时捕获 stdout 和 stderr
# text=True: 直接返回字符串而非 bytes
# timeout=10: 防止命令卡死导致程序挂起
result = subprocess.run(
[cmd_path, "info"],
capture_output=True,
text=True,
timeout=10,
env=os.environ # 显式继承当前环境变量
)
# 4. 合并输出 (有些错误信息在 stderr有些正常输出在 stdout)
# 注意npu-smi 正常时 stderr 通常为空,但为了调试建议都看看
output = result.stdout.strip()
error = result.stderr.strip()
# 如果返回码不为 0说明执行出错 (可能是权限问题)
if result.returncode != 0:
# 如果是权限问题npu-smi 通常会提示 "Permission denied" 或类似信息
return f"exec_error(code={result.returncode}): {error or output}"
if not output:
return "empty_output"
# 5. 优化过滤逻辑 (保留原逻辑但增加容错)
lines = output.split("\n")
lines_filter = []
has_table = False
for line in lines:
# 只要包含 | 就认为是表格行
if "|" in line:
lines_filter.append(line.strip())
has_table = True
# 【改进】如果过滤后为空,但原始输出不为空,说明格式可能变了,不要直接丢弃,返回原始内容供调试
if not has_table:
# 可以选择返回原始输出,或者标记为格式未知
# return output
pass
final_str = ",".join(lines_filter)
return final_str if final_str else output
except subprocess.TimeoutExpired:
return "timeout"
except PermissionError:
return "permission_denied_need_root"
except Exception as e:
return f"run_exception: {str(e)}"
def getMachineRknpu(self):
# cat /sys/kernel/debug/rknpu/load
if self.getSystemName() == "Windows":
__str = ""
else:
try:
if os.path.exists("/sys/kernel/debug/rknpu/load"):
result = os.popen("cat /sys/kernel/debug/rknpu/load")
__str = str(result.read()).strip()
if len(__str) > 0:
lines = __str.split("\n")
__str = ",".join(lines)
else:
__str = ""
else:
__str = "no"
except:
__str = "run error"
return __str