yamon 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
yamon/__init__.py ADDED
@@ -0,0 +1,2 @@
1
+ # Backend package
2
+
yamon/api/__init__.py ADDED
@@ -0,0 +1,2 @@
1
+ # API package
2
+
yamon/api/metrics.py ADDED
@@ -0,0 +1,55 @@
1
+ """Metrics API endpoints"""
2
+
3
+ from fastapi import APIRouter
4
+ try:
5
+ from yamon.collectors.collector import MetricsCollector
6
+ from yamon.history import MetricsHistory
7
+ except ImportError:
8
+ from collectors.collector import MetricsCollector
9
+ from history import MetricsHistory
10
+ from typing import Optional
11
+
12
+ router = APIRouter()
13
+ collector = MetricsCollector()
14
+ history = MetricsHistory(max_size=120)
15
+
16
+ @router.get("/metrics")
17
+ async def get_metrics():
18
+ """获取当前系统指标"""
19
+ metrics = collector.collect()
20
+ history.add_metrics(metrics)
21
+ return {
22
+ "cpu_percent": metrics.cpu_percent,
23
+ "cpu_per_core": metrics.cpu_per_core,
24
+ "cpu_count": metrics.cpu_count,
25
+ "memory_percent": metrics.memory_percent,
26
+ "memory_total": metrics.memory_total,
27
+ "memory_used": metrics.memory_used,
28
+ "memory_available": metrics.memory_available,
29
+ "network_sent_rate": metrics.network_sent_rate,
30
+ "network_recv_rate": metrics.network_recv_rate,
31
+ "cpu_power": metrics.cpu_power,
32
+ "gpu_power": metrics.gpu_power,
33
+ "ane_power": metrics.ane_power,
34
+ "system_power": metrics.system_power,
35
+ "gpu_usage": metrics.gpu_usage,
36
+ "gpu_freq_mhz": metrics.gpu_freq_mhz,
37
+ "ane_usage": metrics.ane_usage,
38
+ }
39
+
40
+ @router.get("/history")
41
+ async def get_history():
42
+ """获取历史数据"""
43
+ return {
44
+ "cpu_percent": history.cpu_percent.get_values(),
45
+ "memory_percent": history.memory_percent.get_values(),
46
+ "network_sent_rate": history.network_sent_rate.get_values(),
47
+ "network_recv_rate": history.network_recv_rate.get_values(),
48
+ "cpu_power": history.cpu_power.get_values(),
49
+ "gpu_power": history.gpu_power.get_values(),
50
+ "ane_power": history.ane_power.get_values(),
51
+ "system_power": history.system_power.get_values(),
52
+ "gpu_usage": history.gpu_usage.get_values(),
53
+ "ane_usage": history.ane_usage.get_values(),
54
+ }
55
+
yamon/api/websocket.py ADDED
@@ -0,0 +1,138 @@
1
+ """WebSocket API for real-time metrics"""
2
+
3
+ from fastapi import APIRouter, WebSocket, WebSocketDisconnect
4
+ try:
5
+ from yamon.collectors.collector import MetricsCollector, SystemMetrics
6
+ from yamon.history import MetricsHistory
7
+ except ImportError:
8
+ from collectors.collector import MetricsCollector, SystemMetrics
9
+ from history import MetricsHistory
10
+ import asyncio
11
+ import json
12
+ from typing import Optional
13
+
14
+ router = APIRouter()
15
+ collector = MetricsCollector()
16
+ history = MetricsHistory(max_size=120)
17
+
18
+ # 共享的最新指标数据(线程安全)
19
+ _latest_metrics: Optional[SystemMetrics] = None
20
+ _metrics_lock = asyncio.Lock()
21
+
22
+ # 后台收集任务
23
+ _collection_task: Optional[asyncio.Task] = None
24
+
25
+ async def _background_collector():
26
+ """后台任务:持续收集指标数据"""
27
+ global _latest_metrics
28
+ while True:
29
+ try:
30
+ # 在线程池中运行同步的collect()方法,避免阻塞事件循环
31
+ metrics = await asyncio.to_thread(collector.collect)
32
+ async with _metrics_lock:
33
+ _latest_metrics = metrics
34
+ history.add_metrics(metrics)
35
+ # 收集间隔:1秒(1fps)
36
+ await asyncio.sleep(1.0)
37
+ except Exception as e:
38
+ print(f"Background collector error: {e}")
39
+ await asyncio.sleep(0.5)
40
+
41
+ async def start_background_collector():
42
+ """启动后台收集任务"""
43
+ global _collection_task
44
+ if _collection_task is None or _collection_task.done():
45
+ _collection_task = asyncio.create_task(_background_collector())
46
+
47
+ @router.websocket("/metrics")
48
+ async def websocket_metrics(websocket: WebSocket):
49
+ """WebSocket 实时推送系统指标"""
50
+ await websocket.accept()
51
+
52
+ # 确保后台收集任务已启动
53
+ await start_background_collector()
54
+
55
+ try:
56
+ while True:
57
+ # 读取最新数据(不等待收集完成)
58
+ async with _metrics_lock:
59
+ metrics = _latest_metrics
60
+
61
+ if metrics is None:
62
+ # 如果还没有数据,等待一下
63
+ await asyncio.sleep(0.1)
64
+ continue
65
+
66
+ # 计算 P 核和 E 核的使用率
67
+ cpu_count = metrics.cpu_count
68
+ cpu_per_core = metrics.cpu_per_core
69
+
70
+ # 根据 CPU 核心数判断 P 核和 E 核数量
71
+ if cpu_count == 8:
72
+ # M1/M2/M3: 4P + 4E
73
+ p_core_count = 4
74
+ e_core_count = 4
75
+ elif cpu_count == 10:
76
+ # M1 Pro/Max: 8P + 2E
77
+ p_core_count = 8
78
+ e_core_count = 2
79
+ elif cpu_count == 12:
80
+ # M2 Pro/Max: 8P + 4E 或 M3 Pro: 6P + 6E
81
+ # 默认假设 8P + 4E
82
+ p_core_count = 8
83
+ e_core_count = 4
84
+ elif cpu_count == 16:
85
+ # M3 Max: 12P + 4E
86
+ p_core_count = 12
87
+ e_core_count = 4
88
+ else:
89
+ # 默认:前一半是 P 核
90
+ p_core_count = cpu_count // 2
91
+ e_core_count = cpu_count - p_core_count
92
+
93
+ # 计算 P 核和 E 核的使用率
94
+ p_cores = cpu_per_core[:p_core_count] if len(cpu_per_core) >= p_core_count else []
95
+ e_cores = cpu_per_core[p_core_count:] if len(cpu_per_core) > p_core_count else []
96
+
97
+ # 计算 P 核和 E 核对总CPU使用率的贡献百分比
98
+ # cpu_percent 是所有核心的平均使用率,即 sum(cpu_per_core) / cpu_count
99
+ # 所以 P核贡献 = sum(p_cores) / cpu_count
100
+ # E核贡献 = sum(e_cores) / cpu_count
101
+ # P核贡献 + E核贡献 = cpu_percent
102
+ if cpu_count > 0:
103
+ cpu_p_percent = (sum(p_cores) / cpu_count) if p_cores else 0.0
104
+ cpu_e_percent = (sum(e_cores) / cpu_count) if e_cores else 0.0
105
+ else:
106
+ cpu_p_percent = 0.0
107
+ cpu_e_percent = 0.0
108
+
109
+ # 发送数据
110
+ await websocket.send_json({
111
+ "cpu_percent": metrics.cpu_percent,
112
+ "cpu_per_core": metrics.cpu_per_core,
113
+ "cpu_count": metrics.cpu_count,
114
+ "cpu_p_percent": cpu_p_percent,
115
+ "cpu_e_percent": cpu_e_percent,
116
+ "memory_percent": metrics.memory_percent,
117
+ "memory_total": metrics.memory_total,
118
+ "memory_used": metrics.memory_used,
119
+ "memory_available": metrics.memory_available,
120
+ "network_sent_rate": metrics.network_sent_rate,
121
+ "network_recv_rate": metrics.network_recv_rate,
122
+ "cpu_power": metrics.cpu_power,
123
+ "gpu_power": metrics.gpu_power,
124
+ "ane_power": metrics.ane_power,
125
+ "system_power": metrics.system_power,
126
+ "gpu_usage": metrics.gpu_usage,
127
+ "gpu_freq_mhz": metrics.gpu_freq_mhz,
128
+ "ane_usage": metrics.ane_usage,
129
+ })
130
+
131
+ # 等待 1 秒(1fps)
132
+ await asyncio.sleep(1.0)
133
+
134
+ except WebSocketDisconnect:
135
+ pass
136
+ except Exception as e:
137
+ print(f"WebSocket error: {e}")
138
+
yamon/cli.py ADDED
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env python3
2
+ """Command-line interface for Yamon"""
3
+
4
+ import argparse
5
+ import sys
6
+ import uvicorn
7
+ from pathlib import Path
8
+
9
+
10
+ def main():
11
+ """Main entry point for yamon command"""
12
+ parser = argparse.ArgumentParser(
13
+ description="Yamon - Visually pleasing, deep system monitoring for macOS"
14
+ )
15
+ parser.add_argument(
16
+ "--host",
17
+ default="0.0.0.0",
18
+ help="Host to bind to (default: 0.0.0.0)",
19
+ )
20
+ parser.add_argument(
21
+ "--port",
22
+ type=int,
23
+ default=8000,
24
+ help="Port to bind to (default: 8000)",
25
+ )
26
+ parser.add_argument(
27
+ "--reload",
28
+ action="store_true",
29
+ help="Enable auto-reload (development mode)",
30
+ )
31
+
32
+ args = parser.parse_args()
33
+
34
+ # Import app after parsing args
35
+ from yamon.main import app
36
+
37
+ uvicorn.run(
38
+ app,
39
+ host=args.host,
40
+ port=args.port,
41
+ reload=args.reload,
42
+ )
43
+
44
+
45
+ if __name__ == "__main__":
46
+ main()
47
+
@@ -0,0 +1,2 @@
1
+ # Collectors package
2
+