#!/usr/bin/env python3
"""光湖蒸馏实时监控 v1.0 · 在AutoDL终端直接运行
   运行方式: python3 watch_distill.py
   功能: 实时滚动展示训练数据，不用手动刷新
   主权: 冰朔 TCS-0002∞ · 铸渊 ICE-GL-ZY001 守护
"""
import os, sys, time, json, subprocess
from datetime import datetime

LOG = '/root/autodl-tmp/distill_mother.log'
STATUS = '/root/autodl-tmp/training_status.json'
WATCHDOG = '/root/autodl-tmp/watchdog.log'

def get_gpu_info():
    try:
        r = subprocess.run(
            ['nvidia-smi', '--query-gpu=temperature.gpu,utilization.gpu,memory.used,memory.total',
             '--format=csv,noheader'],
            capture_output=True, text=True, timeout=5)
        parts = r.stdout.strip().split(', ')
        return {'temp': parts[0], 'util': parts[1], 'mem_used': parts[2], 'mem_total': parts[3]}
    except:
        return None

def get_last_losses(n=10):
    if not os.path.isfile(LOG):
        return []
    losses = []
    with open(LOG) as f:
        for line in f:
            if 'loss=' in line:
                losses.append(line.strip())
    return losses[-n:]

def get_current_progress():
    if not os.path.isfile(LOG):
        return None
    with open(LOG) as f:
        for line in f:
            pass
        last = line.strip() if line else ''
    return last

def get_watchdog():
    if not os.path.isfile(WATCHDOG):
        return None
    with open(WATCHDOG) as f:
        for line in f:
            pass
        return line.strip() if line else None

def get_status():
    if not os.path.isfile(STATUS):
        return None
    with open(STATUS) as f:
        return json.load(f)

def draw_progress_bar(pct, width=30):
    filled = int(pct * width / 100)
    bar = '█' * filled + '░' * (width - filled)
    return f'[{bar}] {pct:.0f}%'

def main():
    print('\033[2J\033[H', end='')  # clear
    print('═══ 光湖蒸馏实时监控 v1.0 ═══')
    print('═══ 主权：冰朔 TCS-0002∞ · 铸渊守护 ═══')
    print('（每5秒自动刷新 · Ctrl+C 退出）')
    print()

    last_loss_count = 0
    loss_history = []

    try:
        while True:
            # 移动光标到顶部
            print('\033[H', end='')

            gpu = get_gpu_info()
            progress = get_current_progress()
            wd = get_watchdog()
            st = get_status()
            losses = get_last_losses(20)

            now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')

            # === GPU状态 ===
            if gpu:
                temp = int(gpu['temp'])
                temp_warn = ' ⚠ 偏高' if temp > 80 else ' ✓'
                mem_u = int(gpu['mem_used'].replace(' MiB', ''))
                mem_t = int(gpu['mem_total'].replace(' MiB', ''))
                mem_pct = mem_u * 100 // mem_t if mem_t > 0 else 0
                mem_warn = ' ⚠ 高负载' if mem_pct > 95 else ' ✓'

                print(f'┌─ GPU 状态 ─────────────────────────────┐')
                print(f'│ 温度: {temp}°C{temp_warn}')
                print(f'│ 利用率: {gpu["util"]}')
                print(f'│ 显存: {mem_u}MB / {mem_t}MB ({mem_pct}%){mem_warn}')
                print(f'└────────────────────────────────────────┘')
            else:
                print('┌─ GPU ──────────────────────────────────┐')
                print('│ ❌ 无法获取GPU信息')
                print('└────────────────────────────────────────┘')

            print()

            # === 蒸馏进度 ===
            print('┌─ 蒸馏进度 ─────────────────────────────┐')
            if progress:
                # 解析 Ep 和步数
                if 'Ep' in progress and '/' in progress:
                    ep_part = progress.split('|')[0].strip() if '|' in progress else progress.split()[0]
                    step_part = progress.split('|')[1].strip().split()[0] if '|' in progress else ''
                    print(f'│ 当前: {ep_part}')
                    if '/' in step_part:
                        cur, total = step_part.split('/')
                        cur, total = int(cur), int(total)
                        pct = cur * 100 / total if total > 0 else 0
                        print(f'│ 步数: {cur}/{total}')
                        print(f'│ 进度: {draw_progress_bar(pct)}')

                # 最近loss行
                if losses:
                    last_loss = losses[-1]
                    print(f'│ Loss: {last_loss}')
            else:
                print('│ ⏳ 等待蒸馏开始...')
            print('└────────────────────────────────────────┘')
            print()

            # === Loss趋势 ===
            if losses:
                print('┌─ 最近Loss趋势 ─────────────────────────┐')
                # 找出loss值的范围
                loss_vals = []
                for l in losses:
                    try:
                        # 提取 loss= 后面的数字
                        idx = l.index('loss=')
                        end = l.index(' ', idx) if ' ' in l[idx:] else len(l)
                        val = float(l[idx+5:end])
                        loss_vals.append(val)
                    except:
                        pass

                if loss_vals:
                    min_l, max_l = min(loss_vals), max(loss_vals)
                    rng = max_l - min_l if max_l > min_l else 1
                    # 打印mini图表
                    for i, v in enumerate(loss_vals):
                        rel = (v - min_l) / rng
                        bar_len = int((1 - rel) * 20)  # loss越低越长
                        marker = '↓' if i > 0 and v < loss_vals[i-1] else ('↑' if i > 0 and v > loss_vals[i-1] else '→')
                        print(f'│ {marker} loss={v:.4f} {"█"*bar_len}')
                print('└────────────────────────────────────────┘')
                print()

            # === 看门狗 ===
            if wd:
                print(f'┌─ 看门狗 ───────────────────────────────┐')
                print(f'│ {wd}')
                print(f'└────────────────────────────────────────┘')
                print()

            # === 状态摘要 ===
            if st:
                mm = st.get('mother_model', {})
                gs = mm.get('gpu', {})
                print(f'┌─ 系统状态 ─────────────────────────────┐')
                print(f'│ 时间: {now}')
                print(f'│ 母模型: {mm.get("status_label", "?")}')
                print(f'│ 蒸馏状态: {"🟢 运行中" if mm.get("status") == "training" else "⚪ 待开始/已完成"}')
                print(f'│ GPU温度: {gs.get("temp_c", "?")}°C')
                print(f'│ 显存: {gs.get("mem_used_mb", 0)//1024}GB/{gs.get("mem_total_mb", 0)//1024}GB')
                print(f'└────────────────────────────────────────┘')

            # 刷新间隔
            time.sleep(5)

    except KeyboardInterrupt:
        print('\n\n监控已退出。蒸馏继续在后台运行。')
        print('重新查看: tail -f /root/autodl-tmp/distill_mother.log')
        sys.exit(0)

if __name__ == '__main__':
    main()
