import time import json import asyncio import logging from aiohttp import web from ..utils.system_info import SystemInfoCollector collector = SystemInfoCollector() logger = logging.getLogger(__name__) # Track active system-status WS clients _sys_ws_clients: set = set() def setup_routes(app, prefix=''): app.router.add_get(f'{prefix}/api/framework', get_framework) app.router.add_get(f'{prefix}/api/system', get_system) app.router.add_get(f'{prefix}/api/system/ws', sys_ws_handler) logger.info(f"📡 系统状态WS端点已注册: {prefix}/api/system/ws") def _read_version(): """Read version from config file (shared helper)""" ver = "v0.6.0" try: import yaml, os config_path = os.path.join(os.path.dirname(__file__), "..", "..", "..", "config", "framework", "base_config.yaml") with open(config_path) as f: cfg = yaml.safe_load(f) ver = cfg.get("framework", {}).get("version", ver) except: pass return ver def _get_framework_data(sm): """Collect framework status (reused by HTTP and WS handlers)""" ver = _read_version() ps = sm.get_service("plugin") if sm else None uptime = int(time.time() - getattr(sm, 'start_time', time.time())) return { "version": ver, "uptime": uptime, "plugins": len(ps.plugins) if ps else 0 } async def get_framework(req): sm = req.app.get('service_manager') if not sm: return web.json_response({"error": "Missing"}, status=500) return web.json_response(_get_framework_data(sm)) async def get_system(req): return web.json_response(collector.get_all()) async def sys_ws_handler(req): """WebSocket push endpoint: system+framwork stats every 2s (no logging per message)""" ws = web.WebSocketResponse(heartbeat=30.0) await ws.prepare(req) _sys_ws_clients.add(ws) async def push(): """Send one snapshot to this client (silent on error)""" try: sm = req.app.get('service_manager') payload = json.dumps({ "type": "sys", "system": collector.get_all(), "framework": _get_framework_data(sm) }, ensure_ascii=False) if not ws.closed: await ws.send_str(payload) except Exception: pass # Background push loop — runs until client disconnects async def push_loop(): while not ws.closed: await push() await asyncio.sleep(2) task = asyncio.ensure_future(push_loop()) try: async for msg in ws: if msg.type == web.WSMsgType.ERROR: break finally: task.cancel() try: await task except asyncio.CancelledError: pass _sys_ws_clients.discard(ws) return ws