"""VPS 引擎数清点(vps_restart_resident.sh 核验用,2026-08-30 live_17/20 教训)。 用法(promote 已部署到 VPS C:\\sanguo_vnpy_v2\\scripts\\ci\\ 后,经 ssh 调): python vps_count_engines.py expected # 杀树前:DB running 数(期望值) python vps_count_engines.py actual # 重拉后:实际引擎进程数 输出一行 ``LIVE= SHADOW=``。两种模式必须分开调——expected 必须在杀树 之前取,重拉后 DB 的 status 已被死亡事件改写,再查就不是期望了。 自匹配陷阱:本进程(wmic 的父 python)命令行只含本脚本路径,不含 ``sanguo_portfolio.runner_live`` / ``sanguo_trader.shadow --account`` 字样, wmic.exe 又被 name='python.exe' 过滤——计数不会把自己算进去(08-30 手工 wmic 探测时 -c 脚本文本含关键字曾 +1 误计)。 """ import sqlite3 import subprocess import sys DB = r"C:\sanguo_vnpy_v2\data\backtest_results.db" def _expected() -> tuple[int, int]: c = sqlite3.connect(DB) live = c.execute( "select count(*) from live_accounts where status='running'").fetchone()[0] shadow = c.execute( "select count(*) from paper_accounts where status='running'").fetchone()[0] c.close() return live, shadow def _actual() -> tuple[int, int]: out = subprocess.run( ["wmic", "process", "where", "name='python.exe'", "get", "CommandLine", "/format:csv"], capture_output=True, text=True, errors="replace").stdout lines = out.splitlines() live = sum("sanguo_portfolio.runner_live" in l for l in lines) shadow = sum("sanguo_trader.shadow --account" in l for l in lines) return live, shadow if __name__ == "__main__": mode = sys.argv[1] if len(sys.argv) > 1 else "" live, shadow = _expected() if mode == "expected" else _actual() print(f"LIVE={live} SHADOW={shadow}")