
    uj                        d Z ddlmZ ddlZddlZddlZddlmZ ddlmZ ddl	m
Z
 ddlmZmZ ddlZddlmZmZmZ dd	lmZ  G d
 d      Zy)u  yjm_ai_snapshot_ring.py

CCTV 순환녹화 방식의 AI snapshot ring.

- 현재 캡처 source를 주기적으로 확인
- 이전 저장 후보와 비교해 일정 변화율 이상일 때만 저장
- latest.jpg, manifest.json, snapshots/*.jpg 관리
- 최근 keep_count개만 유지

주의: Google Drive API 직접 업로드가 아니라 지정 폴더에 저장한다.
지정 폴더가 Google Drive for Desktop 동기화 폴더이면 클라이언트가 자동 업로드한다.
    )annotationsN)asdict)datetime)Path)CallableOptional)Image
ImageChops	ImageStat)SnapshotSettingsc                      e Zd ZdddZddZddZddZddZedd       Z	edd       Z
edd	       Zdd
ZddZddZy)SnapshotRingWorkerNc                    || _         || _        |xs d | _        t               | _        d| _        d | _        t        j                         | _	        d | _
        d| _        d| _        y )Nc                     y N )_msgs    +D:\_St\RTC8790_8791\yjm_ai_snapshot_ring.py<lambda>z-SnapshotRingWorker.__init__.<locals>.<lambda>"   s        F        r   )source_managerloggerstatus_callbackr   settingsrunningthread	threadingLock_lock_last_compare_img_last_saved_at	_save_seq)selfr   r   r   s       r   __init__zSnapshotRingWorker.__init__   s\    ,.E3D(*26^^%
8<!r   c                    | j                   5  || _        d d d        |j                  r| j                          y | j	                          y # 1 sw Y   7xY wr   )r    r   enabledstartstop)r$   r   s     r   update_settingsz"SnapshotRingWorker.update_settings+   s8    ZZ$DM JJLIIK Zs   AAc                    | j                   ry d| _         t        j                  | j                  d      | _        | j                  j                          | j                  d       y )NT)targetdaemonu   AI 스냅샷 ring 시작)r   r   Thread_runr   r(   _statusr$   s    r   r(   zSnapshotRingWorker.start3   sH    <<&&diiE/0r   c                4    d| _         | j                  d       y )NFu   AI 스냅샷 ring 중지)r   r0   r1   s    r   r)   zSnapshotRingWorker.stop;   s    /0r   c                F    	 | j                  |       y # t        $ r Y y w xY wr   )r   	Exception)r$   msgs     r   r0   zSnapshotRingWorker._status?   s'    	  % 		s    	  c                   t        |xs d      }|dkD  rj| j                  |kD  r[t        dt        | j                  || j                  z  z              }| j	                  ||ft
        j                  j                        S | S )Nr      )intwidthmaxheightresizer	   
ResamplingBILINEAR)img	max_widthnew_hs      r   
_fit_widthzSnapshotRingWorker._fit_widthE   sl    	Q'	q=SYY23szzY-BCDEE::y%0%2B2B2K2KLL
r   c                    d}| j                   |kD  r[t        dt        | j                  || j                   z  z              }| j	                  ||ft
        j                  j                        } | j                  d      S )N   r7   L)	r9   r:   r8   r;   r<   r	   r=   r>   convert)r?   small_wsmall_hs      r   _compare_imagez!SnapshotRingWorker._compare_imageM   sh     99w!Sw/B!CDEG**gw/1A1A1J1JKC{{3r   c                   | j                   |j                   k7  ryt        j                  | |      }t        j                  |      }|j
                  rt        |j
                  d         nd}t        dt        d|dz  dz              S )N      Y@r   r   g     o@)	sizer
   
differencer   Statmeanfloatr:   min)prevcurdiffstatrO   s        r   _change_percentz"SnapshotRingWorker._change_percentV   sl    99 $$T3/~~d#&*iiuTYYq\"S3E4%<%#7899r   c           
        t        j                          5 }| j                  rw| j                  5  | j                  }d d d        	 | j	                  |       t        j                  t        dt        j                  xs d                   | j                  rwd d d        y # 1 sw Y   hxY w# t
        $ r;}| j                  j                  d|       t        j                  d       Y d }~d }~ww xY w# 1 sw Y   y xY w)Nzsnapshot ring tick failed: %sg      ?g?)mssr   r    r   _tickr4   r   debugtimesleepr:   rP   interval_sec)r$   sctstexcs       r   r/   zSnapshotRingWorker._run`   s    WWY#,,ZZB  $JJsB' 

3sE"//*@S$ABC ,, YZ ! $KK%%&EsKJJsOO$ YsL   C5B"C5B.AC5"B+	'C5.	C271C-(C5-C22C55C>c                   |j                   sy | j                  j                         }|sy |j                  |j                        }t        j                  d|j                  |j                  dd      }| j                  |t        |j                              }| j                  |      }| j                  dn| j                  | j                  |      }t        j                         }| j                  ?|t!        |j"                        k  ry || j$                  z
  t!        |j&                        k  ry || _        || _        | j)                  ||||j*                         y )NRGBrawBGRXrK   )r'   r   
get_targetgrabmonitor_dictr	   	frombytesrL   bgrarB   r8   r@   rI   r!   rV   r[   rP   change_threshold_percentr"   min_save_interval_sec_save_snapshottitle)	r$   r^   r_   r,   shotr?   cmp_imgchangednows	            r   rY   zSnapshotRingWorker._tickl   s   zz$$//1xx++,ooeTYY		5&Iooc3r||#45%%c*119%t?S?STXTjTjls?tiik!!-r::;;T(((51I1I+JJ!(!CWfll;r   c                X   t        |j                        j                         }|j                         st        j                         |z  }|dz  }|j                  dd       | xj                  dz  c_        t        j                         j                  d      }| d| j                  dd}||z  }	|d	z  }
t        d
t        dt        |j                                    }|j                  |	d|d       |j                  |
d|d       t        |j!                  d      d d      }t        dt        |j"                              }||d  D ]  }	 |j%                           t        |j!                  d      d d      }t        j                         j)                  d      d	||t+        t-        |      d      |j.                  |j0                  |D cg c]h  }d|j2                   |j5                         j6                  t        j8                  |j5                         j:                        j)                  d      dj c}t=        |      d	}|dz  j?                  tA        jB                  |dd      d       | jE                  d| d|d d!       y # t&        $ r Y Vw xY wc c}w )"N	snapshotsT)parentsexist_okr7   z%Y%m%d_%H%M%S_04dz.jpgz
latest.jpg   _   JPEGF)formatqualityoptimizez*.jpgc                6    | j                         j                  S r   rU   st_mtimeps    r   r   z3SnapshotRingWorker._save_snapshot.<locals>.<lambda>       QVVX=N=Nr   )keyreversec                6    | j                         j                  S r   r   r   s    r   r   z3SnapshotRingWorker._save_snapshot.<locals>.<lambda>   r   r   seconds)timespec   z
snapshots/)filerL   mtime)	
updated_atlatest
keep_countsource_titlechanged_percentr9   r;   rs   r   zmanifest.json   )ensure_asciiindentzutf-8)encodingu   AI 스냅샷 저장: u    / 변화율 z.2f%)#r   folder
expanduseris_absolutecwdmkdirr#   r   rq   strftimer:   rQ   r8   jpeg_qualitysavesortedglobr   unlinkr4   	isoformatroundrP   r9   r;   namerU   st_sizefromtimestampr   r   
write_textjsondumpsr0   )r$   r?   r_   rp   rm   basesnap_dirtsr   pathr   r|   fileskeepoldr   manifests                    r   rl   z!SnapshotRingWorker._save_snapshot   s]   BII))+!88:$D+%td3!\\^$$_5Qt~~c*$/$$b#b#boo"678fgF%Hx}}W-3NX\]1c"--()<C

  
 x}}W-3NX\]",,.22I2F"!$U7^Q7YYjj  A	 )1FFH,,%33AFFH4E4EFPPZcPd
  r
!
$ 
	++DJJxe\],^ip+q,TF-}ANO-  s   JA-J'	J$#J$r   )r   zOptional[Callable[[str], None]])r   r   returnNone)r   r   )r5   strr   r   )r?   Image.Imager@   r8   r   r   )r?   r   r   r   )rR   r   rS   r   r   rP   )r^   zmss.mssr_   r   r   r   )
r?   r   r_   r   rp   rP   rm   r   r   r   )__name__
__module____qualname__r%   r*   r(   r)   r0   staticmethodrB   rI   rV   r/   rY   rl   r   r   r   r   r      sh    
11       : :
D<0,Pr   r   )__doc__
__future__r   r   r   r[   dataclassesr   r   pathlibr   typingr   r   rX   PILr	   r
   r   yjm_settingsr   r   r   r   r   <module>r      s>    #       % 
 , , )RP RPr   