
    XEjk                     4   d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlZddlmZ ddlmZ  ee          j                                        Zedz  Zedz  ZdZdZd	Zd
dddddgdddddddd	dg ddddddddddg ddd d!d"d#ddd$dd%g ee          ddd&d'ddd(d	dd)d*g d+dd,	d-d.ddd(d	dd/d*g d0dd,	d1d2ddd(d	dd3d*g d4dd,	d5d6d7ddd8d9g d:d;d<d=id>dd?d@dAdddBddCg ee          ddg	Z ej        ej        dD ej        edEF           ej        ej                  gG            ej        dH          Z da!i Z"i Z#i Z$d$Z%dWdIZ&dJ Z'dK Z(dL Z)dM Z*dN Z+dXdOZ,dP Z-dQ Z.dR Z/dS Z0dT Z1dU Z2e3dVk    r e2             dS dS )Yu  
正方元智能广告工作台 - 服务守护进程
功能：
1. 启动全部后端服务（proxy_server 最后启动）
2. 每 10 秒检测各服务端口健康状态
3. 发现崩溃自动重启
4. 日志记录到 guardian.log
5. 打开门户页面

用法：
    python service_guardian.py          # 启动守护 + 自动启动所有服务
    python service_guardian.py --watch  # 仅守护已运行的服务（不自动启动）
    N)datetime)Pathzguardian.logzguardian.pidz%http://192.168.5.250:9000/portal.html
   x   Ollamai,  z	/api/tagsTollamaserve)exeargs)nameport
check_pathrequiredstartupComfyUIi  z/system_statsGETz8C:\AI\ComfyUI_windows_portable\python_embeded\python.exe)z-szComfyUI\main.pyz--windows-standalone-buildz--listen0.0.0.0--port8188z--enable-cors-headerzC:\AI\ComfyUI_windows_portable)r
   r   cwd)r   r   r   check_methodr   cooldownr   IOPainti  /z%C:\Program Files\Python311\python.exe)z-miopaintstartz--modelcv2z--enable-remove-bgz--remove-bg-devicecudaz--devicer   r   8080z--hostr   )r   r   r   r   r   r      数字人v2i>"  z/api/digital-human/v2/status<   zdh_v2_startup.pyzDuix.Avatari   Fduix-avatarwsl)-dr"   --bashz(/mnt/d/AI-Ad-Agent/scripts/run-avatar.sh)	r   r   r   r   r   r   is_wsl
wsl_distror   zDuix.TTSiG  	duix-fish)r$   r)   r%   r&   z&/mnt/d/AI-Ad-Agent/scripts/run-fish.shzDuix.ASRio'  duix-asr)r$   r*   r%   r&   z%/mnt/d/AI-Ad-Agent/scripts/run-asr.shz
GPT-SoVITSi&  z/docs   z&C:\AI\gptsovits_env\Scripts\python.exe)z	api_v2.pyz-ar   z-p9880zD:\AI-Ad-Agent\GPT-SoVITSPYTORCH_CUDA_ALLOC_CONFzmax_split_size_mb:256)r
   r   r   	env_extra   代理服务器(#  z/api/health   zproxy_server.pyz'%(asctime)s [%(levelname)s] %(message)szutf-8)encoding)levelformathandlersGuardianc           
      *   ddl }ddl}ddl}d|  | }	 |j                            ||          }|j                            |d          5 }|j        dk     cddd           S # 1 swxY w Y   dS # |j        j        $ r>}|j	        dk     }	|	s%t                              d|  d	|j	                    |	cY d}~S d}~w|j        j        t          t          t          t           f$ r}
	  |j        |j        |j                  }|                    d
           |                    d| f          }|                                 |dk    rY d}
~
dS t                              d|  d| d           Y d}
~
dS # t,          $ rD}t                              d|  dt/          |          j         d|            Y d}~Y d}
~
dS d}~ww xY wd}
~
wt,          $ r@}
t                              d|  dt/          |
          j         d|
            Y d}
~
dS d}
~
ww xY w)u8  检测端口是否响应 HTTP 请求（超时10秒，适应大模型加载场景）
    
    特殊处理：
    - HTTP 4xx (如 404): 服务在线（只是路径不对）→ 返回 True
    - HTTP 5xx: 服务异常 → 返回 False  
    - 连接错误: TCP 回退检测（兼容 WebSocket 服务如 ASR）
    r   Nzhttp://127.0.0.1:methodr   timeouti  zcheck_port(z) FAIL: HTTP    	127.0.0.1Tz!) FAIL: TCP connect failed (code=)Fz) FAIL: z: )urllib.requesturllib.errorsocketrequestRequesturlopenstatuserror	HTTPErrorcodeloggerdebugURLErrorConnectionRefusedErrorConnectionResetErrorOSErrorTimeoutErrorAF_INETSOCK_STREAM
settimeout
connect_exclose	Exceptiontype__name__)r   r   r9   urllibrA   urlreqrespheokesockresulte2s                 )D:\AI-Ad-Agent\portal\service_guardian.py
check_portrc      s    MMM
0d
0J
0
0Cn$$S$88^##C#44 	%;$	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	% 	%<!   Ws] 	ELLCtCC"'CCDDD						L!#9;O\#   	 6=1CDDDOOA__k4%899FJJLLL{{ttttt[4[[RX[[[\\\uuuuu 	 	 	LLNtNNT"XX5FNN"NNOOO555555555	    H4HHa1AHHQHHIIIuuuuus~   8A2 A%A2 %A))A2 ,A)-A2 2H3B:4H:+H&A!E4!E44
G>5F=3G=GGH5HHc                      	 ddl } d}| j                            |d          }| j                            |d          5 }|j        dk    cddd           S # 1 swxY w Y   dS # t
          $ r Y d	S w xY w)
u   Ollama 特殊检查r   Nzhttp://127.0.0.1:11434/api/tagsr   r8      r:      F)r?   rB   rC   rD   rE   rU   )rX   rY   rZ   r[   s       rb   check_ollamarg      s    /n$$S$77^##C#33 	&t;#%	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	& 	&   uus5   >A%  AA% AA% A A% %
A32A3c                    t          j         t           j        t           j                  5 }	 |                    d           |                    d| f           	 ddd           dS #  Y ddd           dS xY w# 1 swxY w Y   dS )u   检测端口是否被占用   r=   NTF)rA   rP   rQ   rR   connect)r   ss     rb   is_port_in_userl      s    	v~v'9	:	: a	LLOOOII{D)***	       
	                 s(   A9,A&&A6(A96A99A= A=c           	      	   | d         }| d         }t          |          r#t                              d| d| d           dS | d         }|d         }|                    d	g           }|                    d
d          }|                     dd          rddl}|                    d          }|s&t                              d| d          d           dS |                     dd          }		 t          j        g dddd          }
|	r2|	|
j	        vr)t                              d| d          d|	 d           dS n# t          $ r1}t                              d| d          d|            Y d}~dS d}~ww xY w| d         dk    rddl}|                    d          }|s&t                              d| d          d           dS 	 t          j        g dddd          }
d|
j	        vr&t                              d| d          d           dS n=# t          $ r0}t                              d| d          d|            Y d}~nd}~ww xY wt          j                            |          sFddl}|                    |          }|r|}n(t                              d| d          d|            dS |g|z   }t                              d| d          d d!                    |                      	 t          j                                        }| d         d"k    rd#|d$<   |                     dd          rd}d}t$          j        d%k    rMt          j        d&z  }t          j                    }|xj        t          j        z  c_        t          j        |_        t          j        ||t          j        t          j        |||'          }nJt          j        ||t          j        t          j        |t$          j        d%k    rt          j        nd(          }|t8          | d         <   t;          j                    t<          | d         <   |S # t          $ r1}t                              d| d          d)|            Y d}~dS d}~ww xY w)*u   启动单个服务。
    
    启动前会检查端口是否已被监听，避免重复启动。
    如果端口已被占用（无论服务是否健康），都跳过启动，
    交由健康检查循环中的 restart 逻辑处理。
    r   r   [u	   ] 端口 u    已被占用，跳过启动skippedr   r
   r   r   Nr'   Fr   wsl.exeu"   ] wsl.exe 未找到，跳过启动r(    )rp   z--listz--quietTz	utf-16-ler<   )capture_outputr2   r;   u   ] WSL 发行版 'u   ' 未找到，跳过启动u   ] 无法检查 WSL 发行版: r    u6   ] wsl.exe 未找到，跳过启动（WSL2 未安装）r"   u6   ] WSL 发行版 'duix-avatar' 未找到，跳过启动u   ] 可执行文件不存在: u
   ] 启动:  r   zhttps://hf-mirror.comHF_ENDPOINTwin32i   )r   stdoutstderrenvcreationflagsstartupinfo)r   rv   rw   rx   ry   u   ] 启动失败: )rl   rI   infogetshutilwhichwarning
subprocessrunrv   rU   ospathexistsrF   joinenvironcopysysplatformCREATE_NO_WINDOWSTARTUPINFOdwFlagsSTARTF_USESHOWWINDOWSW_HIDEwShowWindowPopenDEVNULL	processestimeservice_start_times)svcr   r   r   r
   r   r   r}   wsl_exedistror`   r^   foundcmdrx   flagsrz   procs                     rb   start_servicer      s    v;Dv;D d IIItIIIJJJy)nG
%.C;;vr""D
++eT
"
"C
 wwx "O,,y)) 	NNNs6{NNNOOO4r**
	^000#k1  F  &55c3v;cccccdddt 	 	 	NNMs6{MM!MMNNN44444	 
V	%	%,,y)) 	NNbs6{bbbccc4		O^000#k1  F FM11f3v;fffgggt 2  	O 	O 	ONNMs6{MM!MMNNNNNNNN	O 7>># S!! 	CCLLKS[KKcKKLLL4%$,C
KK:CK::388C==::;;;)joov;)##!8C 778U## 	 EK|w&&"3j@(466##z'FF##*4*<'#!)!)#'  DD #!)!)=@\W=T=Tj99Z[  D "&	#f++/9;;CK(   9V99a99:::tttttsK   -AD= =
E8&E33E8	AH 
I&II5EQ 
R&Q>>Rc                 .   t                               |           }|rx	 |                                 |                    d           n$#  	 |                                 n#  Y nxY wY nxY wt           | = t
                              d|  d           dS dS )u   停止服务进程r<   r:   rn   u   ] 已停止N)r   r|   	terminatewaitkillrI   r{   )r   r   s     rb   stop_servicer   v  s    ==D 
+	NNIIaI    			dO))))*****
+ 
+s#   *A	 	A*A! A*!A%#A*c                     | d         }t                               |d          dz   t           |<   t           |         }t                              d| d| d           t	          |           t          j        d           t          j        dk    r`t          j        d           dd	l	}	 t          j        d
| d          dddd          }t                      }|j                                                            d          D ]}d| d          |v rd|                                v r|                                                                }t#          |          dk    rp|d         }|                                rT||vrPt          j        d| ddd           |                    |           t                              d| d|            n#  Y nxY wt          j        d           t+          |           }	|	r/|	dk    r)dt           |<   t                              d| d           nM|	dk    r)t                              d| d           dt           |<   nt                              d| d           |	S )u   重启服务r   r   ri   rn   u   ] 第 u    次重启...   ru   Nznetstat -ano | findstr :r   Tr   )shellrr   textr;   
:	LISTENINGr<   ztaskkill /F /PID )r   rr   r;   u%   ] 已终止占用端口的进程 PID=ro   u   ] 重启成功)   ] 端口已被占用，视为已在运行u   ] 重启失败！)crash_countsr|   rI   r   r   r   sleepr   r   rer   r   setrv   stripsplitupperlenisdigitaddr{   r   rF   )
r   r   countr   r`   killed_pidslinepartspidr   s
             rb   restart_servicer     s   v;D%))$22Q6LE
NN7t775777888 JqMMM |w
1				^83v;884dB  F %%K++--33D99 	^ 	^$s6{$$,,

1L1L JJLL..00E5zzQ#Bi;;== ^S-C-C&N+Ds+D+DD9=qJ J J J'OOC000"KK(\D(\(\WZ(\(\]]]	^	D
1 D 2	!!T,,,,----			GGGGHHHT0000111Ks   *D+G Gc           	      ,
   d t           D             }t          d t           D             d          }| rd |D             }nd |D             }t                              d           t                              d           t                              dt	          j                                        d	                      t                              d
t          |           d           t                              d           d}g }|D ]o}|d         }|dk    rt                      }n4|	                    dd          }	t          |d         |d         |	          }|r-t                              d| d|d          d           |dz  }t                              d| d           t          |          }
|
|                    |           |
dk    r$t                              d| d           |dz  }|	                    dd          rd}nt          }t                              d| d| d           d}t          |          D ]}t          j        d           |dk    rt                      }n4|	                    dd          }	t          |d         |d         |	          }|r+t                              d| d |dz    d!           |dz  } n1|dz   d"z  dk    r$t                              d| d#|dz    d!           |sl|	                    d$d%          r8t                              d| d&| d!           |                    |           Qt                              d| d'           q|r| r|d$         r|d         }|	                    dd          }	t          |d         |d         |	          r(t                              d| d|d          d           nt                              d| d           t          |          }
|
rbt          d"          D ]R}t          j        d           t          |d         d(          r&t                              d| d |dz    d!            nSt                              d           t                              d)| d*t          |           d+           |r0t                              d,d-                    |                      t                              d           ||fS ).u   按依赖顺序启动所有服务
    
    启动顺序：Ollama -> ComfyUI -> IOPaint -> GPT-SoVITS -> 数字人v2 -> Duix服务 -> 代理服务器
    代理服务器必须最后启动，因为需要其他服务已经在线
    c                 *    g | ]}|d          dk    |S )r   r/    .0rk   s     rb   
<listcomp>z&start_all_services.<locals>.<listcomp>  s'    GGGq&	5F(F(F(F(F(F    c              3   2   K   | ]}|d          dk    |V  dS )r   r/   Nr   r   s     rb   	<genexpr>z%start_all_services.<locals>.<genexpr>  s1      LLAQvY:K-K-Ka-K-K-K-KLLr   Nc                 "    g | ]}|d          
|S )r   r   r   s     rb   r   z&start_all_services.<locals>.<listcomp>  s!    :::!AjM:A:::r   c                     g | ]}|S r   r   r   s     rb   r   z&start_all_services.<locals>.<listcomp>  s    )))!A)))r   z<============================================================u/     正方元智能广告工作台 - 服务启动u
     时间: z%Y-%m-%d %H:%M:%Su     核心服务: u    个r   r   r   r   r   r   r   r8   rn   u   ] 已在运行 (端口 r>   ri   u   ] 启动中...ro   r   r'   Fi,  u   ] 等待就绪 (最多 u   秒)...u   ] 已就绪 (u   秒)r   u   ] 仍在等待... (r   Tu   ] 启动超时 (u,   ] 未就绪（可选服务，继续执行）/portal.htmlu     启动完成: r   u    核心服务在线u     失败服务: z, )SERVICESnextrI   r{   r   nowstrftimer   rg   r|   rc   r   appendSTARTUP_TIMEOUTranger   r   r   r   )only_required	non_proxy	proxy_svcto_startstarted_countfailed_servicesr   r   already_runningr9   r   wait_secondsreadyis                 rb   start_all_servicesr     s    HGHGGGILLLLLdSSI  *::y:::))y)))
KK
KKABBB
KKKX\^^445HIIKKLLL
KK63x==666777
KKMO 9T 9T6{ 8*nnOOWW^U33F(Vc,6GPVWWWO 	KKGDGGVGGGHHHQM 	,,,,---S!!<""4(((9KKKDKKKLLLQM 778U## 	+LL*LJJJ\JJJKKK|$$ 	D 	DAJqMMMx$77"3v;L0A&QQQ <<<1Q3<<<==="A|q  BBB1BBBCCC 	Twwz4(( TK4KKKKKLLL&&t,,,,RRRRSSS  - 9Z+@  ~u55i'<)@PPP 	KKMDMM69JMMMNNNNKK0D000111 ++D r  AJqMMM!)F"3^DD $D$D$D1Q3$D$D$DEEE KK
KKU=UU3x==UUUVVV HF$))O*D*DFFGGG
KK/))r   c                      g } t           D ]j}|d         }|dk    rt                      }n4|                    dd          }t          |d         |d         |          }|s|                     |           k| S )uB   对所有服务进行健康检查，返回不健康的服务列表r   r   r   r   r   r   r8   )r   rg   r|   rc   r   )	unhealthyr   r   healthyr9   s        rb   health_checkr   &  s    I " "6{ 8"nnGGWW^U33F Vc,.?OOOG 	"S!!!r   c                     t                               d           t                               dt           d           i } 	 	 t          j        t                     t                      }|s|                                  @|D ]}|d         }t                              |d          }|                    dt                    }|t          j                    |z
  z
  }|dk    r#t                               d| d	|d
d           |                     |d          dz   | |<   |dv rdnd}| |         |k    rbt           
                    d| d| |          d           |d         rt          |           d| |<   t                               d| d           n@# t          $ r  t          $ r)}t                               d| d           Y d}~nd}~ww xY w)u   主守护循环u3   守护进程已启动，开始监控服务状态...u   检查间隔: u    秒Tr   r   r   rn   u   ] 冷却中 (z.0fu   秒后开始健康检查)ri   )r    r/      r   u	   ] 连续 u    次无响应，正在重启...r   u   ] (可选服务) 已离线u   守护循环异常: )exc_infoN)rI   r{   CHECK_INTERVALr   r   r   clearr   r|   COOLDOWN_SECONDSr   r   KeyboardInterruptrU   rF   )	consecutive_failuresr   r   r   
start_timesvc_cooldowncooldown_remainingrestart_thresholdr^   s	            rb   guardian_loopr   ;  sA   
KKEFFF
KK5555666)D(	DJ~&&&$I $**,,,  J J6{ 144T1==
"wwz3CDD%1TY[[:5M%N"%))KK hD h h7I h h h hiii-A-E-EdA-N-NQR-R$T* *.1S)S)SAAYZ!'-1BBBNN#qt#q#q6J46P#q#q#qrrr: J',,,56,T22$H$H$H$HIII3J6 ! 	 	 	 	D 	D 	DLL333dLCCCCCCCC	DQ)Ds   =F  DF G.GGc                      t                               d           t          t                                                    D ]} t          |            t                               d           dS )u   退出时清理所有子进程u   正在停止所有服务...u   所有服务已停止N)rI   r{   listr   keysr   )r   s    rb   cleanupr   n  s`    
KK-...Y^^%%&&  T
KK'(((((r   c                     	 t          d          D ]f} t          ddd          r>t          j        t                     t
                              dt                       dS t          j        d           gt
          	                    d	           d
S # t          $ r(}t
                              d|            Y d}~d
S d}~ww xY w)u   打开门户页面   r0   r   r   r8   u   门户页面已打开: Tri   u-   代理服务器未就绪，无法打开门户Fu   打开门户失败: N)r   rc   
webbrowseropen
PORTAL_URLrI   r{   r   r   r   rU   rF   )r   r^   s     rb   open_portalr   v  s    r 	 	A$u=== 
+++BjBBCCCttJqMMMMGHHHu   /A//000uuuuus   AB #/B 
CCCc                     	 t          t          t                    d          at          j        dk    rddl} 	 |                     t                                          | j	        d           n# t          $ rJ t                                           t                              d           t	          j        d           Y nw xY wddl}	 |                    t                                          |j        |j        z             nW# t          $ rJ t                                           t                              d           t	          j        d           Y nw xY wt                              d           t                                           t                              t          t-          j                                         t                                           d }t3          j        |           d }t7          j        |d	
          }|                                 d	S # t<          $ r  t>          $ r(}t                               d|            Y d}~d	S d}~ww xY w)u   获取 PID 文件锁，防止多个守护进程同时运行。
    
    使用文件锁 (Windows: msvcrt, Linux: fcntl) 确保原子性，
    避免竞态条件导致多个守护进程同时启动。
    za+ru   r   Nri   u9   已有守护进程运行（文件锁被占用），退出c                      	 t           r8t                              d           t                                            d a d S d S #  Y d S xY w)NT)
missing_ok)_lock_fdPID_FILEunlinkrT   r   r   rb   cleanup_lockz&acquire_pid_lock.<locals>.cleanup_lock  sV     $OOtO444NN$$$#HHH$ $s   =A Ac                      	 t          j        d           	 t          t          j        r.t                              d           t          j        d           n#  Y nxY w_)NTr1   u   PID 锁丢失，退出守护ri   )r   r   r   closedrI   rF   r   _exitr   r   rb   periodic_lock_checkz-acquire_pid_lock.<locals>.periodic_lock_check  sZ    
2'8?'%CDDDDs   AA ATtargetdaemonu   PID 锁获取异常: )!r   strr   r   r   r   msvcrtlockingfilenoLK_NBLCKrN   rT   rI   rF   exitfcntlflockLOCK_EXLOCK_NBseektruncatewriter   getpidflushatexitregister	threadingThreadr   
SystemExitrU   r   )r  r  r   r   tr^   s         rb   acquire_pid_lockr    s@   =Ht,,<7""MMMx00&/1EEEE      XYYY	 LLLHOO--u}u}/LMMMM      XYYY 	as29;;''(((	 	 	 	%%%		 		 		 $7EEE				t      2q22333tttttsa   6H% 3A- ,H% -AC>H%  CH% 	:D H% AEH% ECH% %I!9II!c                     dd l } |                     d          }|                    ddd           |                    ddd	           |                    d
dd           |                                }t	                       t          j        t                     t          j        t          j	        d            t          j        t          j
        d            |j        rt                              d           n|j         }t          |           |j        s-t#          j        t&          d                                           	 t+                       d S # t,          $ r t                              d           Y d S w xY w)Nr   u3   正方元智能广告工作台 - 服务守护进程)descriptionz--watch
store_trueu0   仅守护已运行的服务（不自动启动）)actionhelpz--no-browseru   不自动打开浏览器z--allu*   启动所有服务（包括可选服务）c                 *    t          j        d          S Nr   r   r  sigframes     rb   <lambda>zmain.<locals>.<lambda>  s    CHQKK r   c                 *    t          j        d          S r  r  r   s     rb   r#  zmain.<locals>.<lambda>  s    SXa[[ r   u'   守护模式：监控已运行的服务)r   Tr   u%   
收到中断信号，正在退出...)argparseArgumentParseradd_argument
parse_argsr  r  r  r   signalSIGINTSIGTERMwatchrI   r{   allr   
no_browserr  r  r   r   r   r   )r%  parserr   r   s       rb   mainr0    s   OOO$$1f$ggF
	,=oppp
|B\]]]
;ghhhD  OG
M&-!?!?@@@
M&."@"@AAAz 8=>>>> !H7777 ? BD999??AAA> > > ><======>s   E $FF__main__)r   r   )F)4__doc__r   r   r   jsonrA   r)  r  loggingr  r   r   r   pathlibr   __file__parentresolve
PORTAL_DIRLOG_FILEr   r   r   r   r  r   basicConfigINFOFileHandlerStreamHandlerrv   	getLoggerrI   r   r   r   r   r   rc   rg   rl   r   r   r   r   r   r   r   r   r  r0  rW   r   r   rb   <module>r@     s    
			 



                               T(^^"**,,
&&4
 !I
 
	 	 %N- - - 5
 
   ;P P P
 
 " 4;'(3z??
 
  #ccc
 
   !___
 
    ]]]
 
   <@@@/)+B	
 
 " "#;&'3z??
 
 kBJ  
,4Hw777cj))    
	:	&	& 	  ( ( ( (V	 	 	  s s sl+ + + / / /dk* k* k* k*\  *0D 0D 0Df) ) )  "D D DN >  >  >F zDFFFFF r   