
    *i                        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mZmZ ddlmZ ddlmZ ddlmZmZmZ ddlZddlZddlmZ ddlmZ dd	lmZmZ dd
l m!Z! ddl"m#Z# ddl$m%Z% ddl&m'Z' ddl(m)Z) ddl*m+Z+ ddl,m-Z- ded         ddfdZ.e G d d                      Z/ G d d          Z0efde1dee%         fdZ2 ej3        d          Z4de1de1dz  fdZ5de6e1e1f         fdZ7de8fd Z9d!edefd"Z:d#ed$e1d%e1ddfd&Z;d'e+d(e'de<fd)Z=dee1         fd*Z>d'e+d(e'de<fd+Z?eed,d-d.dfd/ee1         d0e1d(e'd1e1d2e1d3e1d4e1d5e<d6ee8         ddfd7Z@dS )8u  Demonstration recorder – cameras at 30 fps, robot joints at ~100 Hz.

Thread layout during a recording session::

    teleop-right     : follower_r mirrors leader_r at 100 Hz  (background, per episode)
    teleop-left      : follower_l mirrors leader_l at 100 Hz  (background, per episode)
    camera-<name>    : camera.grab() loop per camera          (active while recording)
    robot-recorder   : reads all joint observations at 100 Hz (active while recording)

Cameras are opened once at session start and stay open across episodes.
Teleop threads are started/stopped per episode.
Camera and robot threads are started/stopped per recording episode.

Output layout::

    data/raw/<task_name>/<episode_idx>/
        metadata.json
        robot_data.npz
        cameras/
            scene_camera.svo2
            left_wrist_camera.svo2
            right_wrist_camera.svo2
            ...

After conversion (``rd convert``) each camera directory is expanded into
PNG frames and depth maps.
    N)asdict	dataclass)datetime)Path)DictListOptional)CLOCK_CAMERA)CLOCK_FALLBACK)CALIBRATION_FILECAMERA_CONFIG)AudioRecorder)CameraConfig)Camera)TeleopInterface)get_db)RobotController)
fzf_selectcamerasr   returntuple[int, str]c                     	 t          | d                                                   t          fS # t          t          t
          f$ r t          j                    t          fcY S w xY w)a8  Read the reference camera clock with fallback.

    Returns ``(ts_ns, clock_label)``. On a healthy ZED setup the camera
    clock matches `robot_data.npz` and converted camera frames; on
    failure we substitute `time.time_ns()` and label the value so
    downstream consumers can detect the inconsistency.
    r   )	intget_current_timestamp_ns_CLOCK_CAMERARuntimeErrorOSError
ValueErrortimetime_ns_CLOCK_FALLBACK)r   s    -/home/robot-lab/raiden_cmu/raiden/recorder.py_capture_clockr#   ;   sc    /71:668899=HH':. / / /|~~..../s   -0 1A$#A$c                       e Zd ZU eed<   eed<   eed<   eed<   eed<   eed<   ee         ed<   eed<   d	Zeed
<   dZ	e
ed<   dZe
ed<   dS )RecordingMetadata	task_nametask_instruction	timestamp
duration_srobot_framesrobot_hzr   
camera_fpsleadercontrolFcomplete	convertedN)__name__
__module____qualname__str__annotations__floatr   r   r.   r/   boolr0        r"   r%   r%   N   s         NNNNNNOOO#YOOOGSHdItr9   r%   c                       e Zd ZdZ	 ddee         dedededede	d	e
d
         fdZddZddedefdZddZd dZdedej        ddfdZdej        ddfdZddZddededdfdZdS )!DemonstrationRecorderu  Manages one recording episode.

    Cameras are opened before this object is created and remain open across
    episodes (opening/closing ZED cameras is slow).  start_recording() /
    stop_recording() toggle the SVO2 writers and the robot data thread.

    stop_recording() does NOT close cameras or shut down robots — the caller
    (run_recording) is responsible for that at session end.
    Nr   robot_controllerrecording_dirr&   r'   	interfaceaudio_recorderr   c                     || _         || _        || _        || _        || _        || _        || _        |dz  | _        d| _        t          j
                    | _        g | _        g | _        d| _        g | _        d S )Nr   F        )r   r<   r=   r&   r'   r>   r?   cameras_diris_recording	threadingEvent_stop_event_threads_robot_frames_start_time_event_markers)selfr   r<   r=   r&   r'   r>   r?   s           r"   __init__zDemonstrationRecorder.__init__m   s      0*" 0",(94!$?,,02 *,"% +-r9   r   c                      j         rd S  j                            dd            j                            dd           d _         t	          j                     _        g  _        g  _         j	        
                                 g  _        i  _        d fdfd j        D             }|D ]}|                                 |D ]}|                                  j        D ]\}t!          j         j        | j	        fd|j         d          }|                                  j                            |           ]t!          j         j         j	         j        d         fd	d          }|                                  j                            |            j         j                             j                   t1          d
           t1          d           t1          d           t1          d           d S )NTparentsexist_okr   c                     j         | j         d| j         z  }t          j                    }|                     |           |j        | j        <   d S )N.)rB   namerecording_extensionr   r    start_recording_camera_start_times_ns)camerapatht0rK   s      r"   
_start_onez9DemonstrationRecorder.start_recording.<locals>._start_one   sW    #&S&Sv7Q&S&SSDB""4(((79D'444r9   c                 @    g | ]}t          j        |fd           S T)targetargsdaemonrD   Thread).0camrZ   s     r"   
<listcomp>z9DemonstrationRecorder.start_recording.<locals>.<listcomp>   s=     
 
 
 JcVDIII
 
 
r9   zcamera-)r]   r^   rS   r_   r   zrobot-recorder=
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!z  RECORDING STARTEDz<!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!z+  Press the button again to stop recording
r   N)rC   r=   mkdirrB   r   	monotonicrI   rH   rJ   rF   clearrG   rV   r   startjoinrD   ra   _camera_looprS   append_robot_loopr?   start_episodeprint)rK   start_threadstrW   rZ   s   `   @r"   rU   z%DemonstrationRecorder.start_recording   sN    	F   ===td;;; >++    68#	: 	: 	: 	: 	: 	:
 
 
 
|
 
 
  	 	AGGIIII 	 	AFFHHHH l 	$ 	$F (d./,v{,,	  A GGIIIM  ####
 #"DLO4!	
 
 
 	
			Q *--d.@AAAo#$$$h<=====r9   Tr/   c                 J   | j         s| j        S d| _         | j                                         t	          j                    | j        z
  }| j                                         | j	        D ]}|
                    d           d | j        D             }|D ]}|                                 |D ]}|
                                 | j        A| j                                         | j                                        st!          d           t!          d           t!          d           t!          d	|d
d           t!          dt#          | j                              t!          d           |                                  |                     ||           | j        S )ut  Stop the current recording episode and persist data.

        Shuts down the robot controller (returns home + closes motor connections)
        and stops camera recording, but does NOT close cameras — they stay open
        so the next episode can start without re-initialising the camera SDK.
        The caller is responsible for calling camera.close() at session end.

        Args:
            complete: Set to True when the episode was cleanly stopped by the
                      user.  Set to False on crash / Ctrl-C so the directory
                      can be detected as incomplete and overridden next run.
        Fg      @)timeoutc                 D    g | ]}t          j        |j        d           S )T)r]   r_   )rD   ra   stop_recording)rb   rW   s     r"   rd   z8DemonstrationRecorder.stop_recording.<locals>.<listcomp>   s;     
 
 
 F$9$GGG
 
 
r9   NzV  ! Audio flush did not complete in time; metadata.json may under-count audio_segmentsre   z  RECORDING STOPPEDz  Duration : .2fz sz  Robot frames : z=!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
r/   )rC   r=   rF   setr   rh   rI   r<   shutdownrG   rk   r   rj   r?   stop_episodewait_until_idlerp   lenrH   _save_robot_data_save_metadata)rK   r/   durationrr   stop_threadss        r"   rv   z$DemonstrationRecorder.stop_recording   s      	&%%!>##d&66
 	&&(((  	  	 AFF3F
 
,
 
 
  	 	AGGIIII 	 	AFFHHHH *,,...
 &6688 C  
 	o#$$$.h....///;#d&8"9"9;;<<<o 	Hx888!!r9   r   c                 *    t          | j                  S )zgRead the reference camera clock with fallback. See module-level
        ``_capture_clock`` for details.)r#   r   )rK   s    r"   r#   z$DemonstrationRecorder._capture_clock  s     dl+++r9   Optional[tuple[int, str]]c                 >   | j         sdS t          }	 t          | j        d                                                   }nf# t
          t          t          f$ rL}t          j	                    }t          }t          dt          |          j         d| d           Y d}~nd}~ww xY wt          j                    | j        z
  }| j                            |t%          |d          |d           t          dt'          | j                   d	|d
d| d| d	           ||fS )ap  Record a timestamped subtask-boundary marker.

        Captures the reference camera's clock so the marker shares the same
        time base as the robot frames and camera frame timestamps. If the
        camera-clock read fails (e.g. transient SDK error), falls back to
        ``time.time_ns()`` and records ``clock="wallclock_fallback"`` so
        downstream consumers can detect the inconsistency.

        Returns the captured ``(t_ns, clock)`` so callers can fan it out
        to other consumers (e.g. ``AudioRecorder.mark_boundary``) without
        a second clock read.  Returns ``None`` if not recording.
        Nr   z+  ! Event marker camera-clock read failed (z: zS); falling back to time.time_ns(). Marker will not align with ZED frame timestamps.   )rr   	elapsed_sclocku     ✓ Event marker #z at rw   zs (ts=z, clock=))rC   r   r   r   r   r   r   r   r   r    r!   rp   typer1   rh   rI   rJ   rm   roundr}   )rK   r   ts_nseelapseds        r"   add_event_markerz&DemonstrationRecorder.add_event_marker  s      	4		Q@@BBCCEEgz2 	 	 	LNNE#E(d1gg>N ( (RS ( ( (       	 .""T%55""eGQ&7&7%HH	
 	
 	
 	+3t':#;#; + +P + ++ +"'+ + +	
 	
 	
 e|s   ,? B"ABB"rW   
stop_eventc                     |                                 s*|                                 |                                 (dS dS )u<   Grab loop – camera SDK rate-limits to its own FPS (30 Hz).N)is_setgrab)rK   rW   r   s      r"   rl   z"DemonstrationRecorder._camera_loopE  sM    ##%% 	KKMMM ##%% 	 	 	 	 	r9   c                    d}|                                 st          j                    }	 |                                }| j                                        }| j                                        }| j                            |||d           n)# t          $ r}t          d|            Y d}~nd}~ww xY wt          j                    |z
  }	||	z
  }
|
dk    rt          j        |
           |                                 dS dS )a  Read joint observations at ~100 Hz and buffer them.

        Timestamps are recorded via ``ref_camera.get_current_timestamp_ns()``.

        - ZED cameras override this to return the ZED SDK hardware clock, which
          is on the same clock as the frame timestamps in the SVO2 file.
          Direct interpolation at conversion time requires no correction.
        - RealSense cameras fall back to ``time.time_ns()`` (system wall clock),
          while frame timestamps use the RealSense hardware clock.  The offset
          between the two clocks is measured once per session and stored in
          ``metadata.json`` as ``realsense_clock_offsets``, then applied at
          conversion time.
        g{Gz?)rr   obscmdz%  Warning: robot observation failed: Nr   )r   r   rh   r   r<   get_all_observationsget_last_commanded_positionsrH   rm   	Exceptionrp   sleep)rK   r   
ref_camera	target_dt
loop_startr   r   r   r   r   
sleep_times              r"   rn   z!DemonstrationRecorder._robot_loopJ  s7    	##%% 	'))JC";;==+@@BB+HHJJ"))c#*N*NOOOO C C CAaAABBBBBBBBC n&&3G"W,JA~~
:&&& ##%% 	' 	' 	' 	' 	's   A$B 
B6B11B6c           
         | j         dz  }t          | j                  }|dk    rt          d           dS i }t	          j        d | j        D             t          j                  |d<   t          | j        d         d                                                   }|D ]ot          | j        d         d                                                            }|D ]2t	          j	        fd	| j        D                       }|| d
 <   3pdD ] d} d}||v r~||v rz||         
                    |d                              t          j                  }	t	          j        ||                             t          j                  |	gd          | d<    d}
fd| j        D             }||v rt          d |D                       st          d d          d}g }t!          |          D ]F\  }}||}||                    |           !|
|v r!|                    ||
         |                    Gt          |          |k    r&t          d dt          |           d| d          t	          j	        |                              t          j                  | d<   t	          j        |fi | t          d| d|            dS )zEFlatten the robot frame list into per-key numpy arrays and save .npz.zrobot_data.npzr   z#  Warning: no robot frames recordedNc                     g | ]
}|d          S )rr   r8   )rb   fs     r"   rd   z:DemonstrationRecorder._save_robot_data.<locals>.<listcomp>x  s    000QsV000r9   )dtype
timestampsr   c                 8    g | ]}|d                            S )r   r8   )rb   r   key
robot_names     r"   rd   z:DemonstrationRecorder._save_robot_data.<locals>.<listcomp>  s'    VVVa% 4S 9VVVr9   _)
follower_r
follower_l
_joint_pos_gripper_pos   )axis_joint_pos_7dc                 F    g | ]}|d                                         S )r   )get)rb   r   arm_keys     r"   rd   z:DemonstrationRecorder._save_robot_data.<locals>.<listcomp>  s)    JJJ!%W--JJJr9   c              3      K   | ]}|d uV  	d S Nr8   rb   cs     r"   	<genexpr>z9DemonstrationRecorder._save_robot_data.<locals>.<genexpr>  s&      ;;Q1D=;;;;;;r9   z$No commanded positions recorded for z5. The teleop loop never issued a command to this arm.z'Could not build complete cmd array for z: got /z frames.
_joint_cmdu     ✓ Robot data saved  (u    frames) → )r=   r}   rH   rp   nparrayint64listkeysstackreshapeastypefloat32concatenateanyr   	enumeraterm   savez_compressed)rK   output_filendatarobot_namesobs_keysarrjp_keygp_keygrip	pos7d_keyraw_cmdsfallbackfilledir   r   r   r   s                   @@@r"   r~   z&DemonstrationRecorder._save_robot_datam  s   (+;;"##667888F&(X00T/000
 
 
\
 4-a07<<>>??% 	2 	2JD.q1%8DIIKKLLH 2 2hVVVVV4CUVVVWW.1
**S**++2 4 #	S #	SG+++F---F~~&D..F|++Aq1188DD24.&\((44d;!3 3 3.../ #111IJJJJt7IJJJH~~;;(;;;;; &Nw N N N    !%h// : :DAq}#$+h////"d**d9oa&8999v;;!##&9' 9 9"6{{9 9-.9 9 9   02x/?/?/F/Frz/R/R+++,
K004000G!GG+GGHHHHHr9   r   c                    | j         dz  }t          | j                  }|dk    r||z  nd}t          | j        | j        t          j                                                    t          |d          |t          |d          d | j
        D             d| j        j        |d	          }t          |          }| j        r
| j        |d
<   d | j
        D             }|r||d<   | j        r
| j        |d<   | j        ?| j                                        }	|	d         r|	d         |d<   |	d         r|	d         |d<   t%          |d          5 }
t'          j        ||
d           d d d            n# 1 swxY w Y   t+          d|            d S )Nmetadata.jsonr   rA   r   r   c                     g | ]	}|j         
S r8   rS   r   s     r"   rd   z8DemonstrationRecorder._save_metadata.<locals>.<listcomp>  s    222QV222r9      F)r&   r'   r(   r)   r*   r+   r   r,   r.   r/   r0   camera_start_times_nsc                 J    i | ] }t          |d d          |j        |j        !S )_clock_offset_nsN)getattrrS   r   )rb   rc   s     r"   
<dictcomp>z8DemonstrationRecorder._save_metadata.<locals>.<dictcomp>  s<     
 
 
s.55A Hc*AAAr9   realsense_clock_offsetsevent_markers
audio_fullaudio_segmentsw   )indentu     ✓ Metadata saved → )r=   r}   rH   r%   r&   r'   r   now	isoformatr   r   r>   rS   r   rV   rJ   r?   drainopenjsondumprp   )rK   r   r/   r   r   hzmeta	meta_dict
rs_offsets
audio_datar   s              r"   r   z$DemonstrationRecorder._save_metadata  s   (?:"##%\\Q\\s n!2lnn..00Xq))2q\\22T\222N'
 
 
 4LL	& 	M151LI-.
 
|
 
 


  	>3=I/0 	=)-)<Io&*,2244J,' C*4\*B	,'*+ K.89I.J	*++s## 	.qIi1----	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	. 	7+7788888s   E++E/2E/r   rf   )T)r   r   )r   r   )r1   r2   r3   __doc__r   r   r   r   r4   r   r	   rL   rU   r7   rv   r#   r   rD   rE   rl   rn   r~   r6   r   r8   r9   r"   r;   r;   b   s        $ 59- -f- *- 	-
 - - #- !1- - - -HC> C> C> C>J@" @"t @"t @" @" @" @"D, , , ,
" " " "P6 y 4    
'io 'd ' ' ' 'F@I @I @I @ID-9 -9u -9 -9 -9 -9 -9 -9 -9 -9r9   r;   config_filec                 >   t          |                                           }i i dt          ddffdfd|D             }|D ]}|                                 |D ]}|                                 rt          d           fd|D             S )z7Create Camera instances for every entry in camera.json.rS   r   Nc           	      &   	                      |           }|                                                     |           }                    |           }t	          d|  d| d| d           || <   d S # t
          $ r}|| <   Y d }~d S d }~ww xY w)Nu     ✓ Camera 'z
' opened (z
, serial: r   )create_camerar   get_camera_typeget_serial_by_namerp   r   )rS   rc   cam_typeserialexccfgerrorsresultss        r"   _openz'load_cameras_from_config.<locals>._open  s    	##D))CHHJJJ**400H++D11FP4PP8PPvPPPQQQGDMMM 	 	 	F4LLLLLLL	s   A1A6 6
B BBc                 @    g | ]}t          j        |fd           S r\   r`   )rb   rS   r   s     r"   rd   z,load_cameras_from_config.<locals>.<listcomp>  s:       FJ	TGDAAA  r9   zCamera initialization failed: c                      g | ]
}|         S r8   r8   )rb   rS   r   s     r"   rd   z,load_cameras_from_config.<locals>.<listcomp>  s    ,,,dGDM,,,r9   )r   list_camera_namesr4   rj   rk   r   )r   namesthreadsrr   r   r   r   r   s       @@@@r"   load_cameras_from_configr    s    {
#
#C!!##EGF	C 	D 	 	 	 	 	 	 	 	   NS  G   					  	 FDFDDEEE -,,,e,,,,r9   z^[A-Za-z0-9_]+$rS   c                 F    | sdS t                               |           sdS dS )u   Return an error message if *name* is invalid, otherwise None.

    Valid names contain only letters, digits, and underscores — no spaces.
    Examples: ``PickUpApple``, ``pick_up_apple``, ``task01``.
    zTask name cannot be empty.zlTask name must contain only letters, digits, and underscores (no spaces). E.g. PickUpApple or pick_up_apple.N)_TASK_NAME_REmatchr   s    r"   validate_task_namer    s4      ,++t$$ ~}}4r9   c                     t                      } |                                 }d}d t          |          D             }t          t	          |          |gz   d          d         }||k    rt          d                                          }t          |          }|r&t          d|            t          j
        d           t          d	                                          }|s#t          d
           t          j
        d           |                     |          }|+|                     ||           |                     |          }|d         |d         fS ||         }|d         |d         fS )z3Use fzf to choose (or create) a task for recording.z<< Add new task >>c                 8    i | ]}|d           d|d          d|S )rS   z  (instructionr   r8   rb   rr   s     r"   r   zselect_task.<locals>.<dictcomp>#  s4    OOO!622q/222AOOOr9   zRecord task> promptr   z  New task name: zError: r   z  Task instruction: z'Error: task instruction cannot be emptyNrS   r	  )r   	get_tasksreversedr   r   inputstripr  rp   sysexitget_task_by_nameadd_task)	dbtasks_NEWlabelschosenrS   errr	  tasks	            r"   select_taskr    sa   	BLLNNEDOOxOOOFVv-oFFFqIF~~())//11 && 	/C//"""HQKKK23399;; 	;<<<HQKKK""4((<KKk***&&t,,DF|T-000&>D<m,,,r9   c                     t                      } |                                 }d}d t          |          D             |gz   }t          |d          d         }||k    rzt	          d                                          }|s#t          d           t          j        d           | 	                    |          }|r|d	         S | 
                    |          S | 	                    |          }|r|d	         S | 
                    |          S )
zCUse fzf to choose (or create) a teacher; returns the DB teacher id.z<< Add new teacher >>c                     g | ]
}|d          S r   r8   r
  s     r"   rd   z"select_teacher.<locals>.<listcomp>@  s    444Aai444r9   zSelect teacher> r  r   z  New teacher name: z#Error: teacher name cannot be emptyr   id)r   get_teachersr  r   r  r  rp   r  r  get_teacher_by_nameadd_teacher)r  teachersr  r  r  rS   existings          r"   select_teacherr%  :  s   	B  H"D44(!3!3444v=F'9:::1=F~~+,,2244 	7888HQKKK))$// 	"D>!~~d###%%f--H ~>>&!!!r9   task_dirc                    t          d |                                 D                       }|r|d         }|dz  }d}|                                rRt          |          5 }t	          j        |          }ddd           n# 1 swxY w Y   |                    dd           }|r<t          d|            t          j	        |           |
                                 |S t          |          d	}| |z  }|
                                 |S )
zReturn the directory to record the next episode into.

    If the last episode directory has no metadata or ``complete=False``, it is
    treated as an incomplete recording, wiped, and reused.  Otherwise a new
    numbered directory is created.
    c              3   t   K   | ]3}|                                 |j                                        /|V  4d S r   )is_dirrS   isdigit)rb   ds     r"   r   z&_next_recording_dir.<locals>.<genexpr>`  s@      WWAqxxzzWafnnFVFVWaWWWWWWr9   r   TNr/   Fz#  Overriding incomplete recording: 04d)sortediterdirexistsr   r   loadr   rp   shutilrmtreerg   r}   )	r&  r$  last_dir	meta_fileis_incompleter   r   episode_idxnew_dirs	            r"   _next_recording_dirr9  Y  sR    WW!1!1!3!3WWWWWH B<.	 	<i $Ay||$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $U ; ;;M 	BBBCCCM(###NNO]]((K$GMMOOONs    BBBr=   bucketprefixc           	         t          j        d          }t          |                     d                    D ]y}|                                s|                    | j                  }| d| }t          d|j         d| d|            |	                    t          |          ||           zt          d| d| d           d S )Ns3*r   z  Uploading u
    → s3://u   
✓ Upload complete → s3://)boto3clientr.  rglobis_filerelative_toparentrp   rS   upload_filer4   )r=   r:  r;  r=  	file_pathrelr   s          r"   upload_to_s3rH  |  s    	d		BM//4455 4 4	  "" 	##M$899#EY^EEvEEEEFFF
s9~~vs3333	
>F
>
>V
>
>
>?????r9   r<   r>   c                    t           j                                        }t          j        |          }	 t          j        |           	 | j        r#	 t          j        |t          j	        |           dS |
                    |           r#	 t          j        |t          j	        |           dS t          j        t           j        gg g d          d         rt           j                            d          }|                                dk    r#	 t          j        |t          j	        |           dS |dv r#	 t          j        |t          j	        |           dS t          j        d           !# t          j        |t          j	        |           w xY w)u   Wait for Enter/Space (proceed) or 'q' (quit session).

    Returns:
        True  — 'q' pressed or footpedal e-stop; caller should end the session.
        False — Enter/Space or footpedal left fired; caller should proceed.
    TFr   r   q
 皙?r  stdinfilenotermios	tcgetattrtty	setcbreaksession_estop_requested	tcsetattr	TCSADRAINpollselectreadlowerr   r   r<   r>   fdold_settingschs        r"   _wait_for_enter_or_quitrb    s~    
				B$R((L?b	7  	"g/>>>>> ~~.//  	"g/>>>>> }ci["b!44Q7 !Y^^A&&88::$$
 	"g/>>>>>	 ***  	"g/>>>>> Jt	 	"g/>>>>s$   E* 3E* +AE* -E* E* *"Fc                     t          d           t          d           t          d           t          d           t          j                                        } t	          j        |           }	 t          j        |            t          j	                    dz   }t          j	                    |k     rt          j
        t          j        gg g d          d         rt          j                            d          }|dv r#	 t	          j        | t          j        |           d	S |                                d
k    r#	 t	          j        | t          j        |           dS 	 t	          j        | t          j        |           dS t          j        d           t          j	                    |k     t	          j        | t          j        |           n%# t	          j        | t          j        |           w xY wdS )u%  After recording stops, wait for a keyboard verdict.

    Inputs accepted:
      - Enter key               → "success"
      - 'f' key                 → "failure"
      - Any other key / timeout → None (leaves status as "pending")

    Returns:
        "success", "failure", or None.
    z=
------------------------------------------------------------z  Mark this demonstration:u:       Enter → success   f → failure   other key → skipz=------------------------------------------------------------
g      >@r   r   )rL  rM  successr   failureNrO  )rp   r  rQ  rR  rS  rT  rU  rV  r   rh   r[  r\  rX  rY  r]  r   )r_  r`  deadlinera  s       r"   _wait_for_verdictrg    s    
/	
&'''	
FGGG	/					B$R((L?b>##d*n))}ci["b!44Q7 Y^^A&&%%$ 	"g/>>>>> 88::$$$ 	"g/>>>>>  	"g/>>>>> Jt n)) 	"g/>>>>"g/>>>>4s   0BG	 G	 =+G	 	"G+c                    t           j                                        }t          j        |          }	 t          j        |           	 | j        r#	 t          j        |t          j	        |           dS |
                    |           r#	 t          j        |t          j	        |           dS t          j        t           j        gg g d          d         rZt           j                            d          }|                                dk    r#	 t          j        |t          j	        |           dS t          j        d           # t          j        |t          j	        |           w xY w)u   Wait for a leader-arm button press (start) or the 'q' key (quit session).

    Returns:
        True  — 'q' pressed or session e-stop; caller should end the session.
        False — leader-arm button pressed; caller should start recording.
    TFr   r   rJ  rO  rP  r^  s        r"   _wait_for_start_or_quitri    sM    
				B$R((L?b		7  	"g/>>>>> ~~.//  	"g/>>>>> }ci["b!44Q7  Y^^A&&88::$$ 	"g/>>>>> Jt		 	"g/>>>>s   E 3E +AE -E "E$bimanualr   F	s3_bucket	s3_prefixcamera_config_filecalibration_filearmsdata_dirrecord_audioaudio_device_indexc	           	      B  . t          d           t          d           t          d           t                      \  }	}
t          |          dz  |	z  }|                    dd           t	                      }t          d|	            t          d|
 d	           t                      }	 t          |          5 }t          j        |          }d
d
d
           n# 1 swxY w Y   |	                    ||          }n&# t          $ r |	                    i |          }Y nw xY w|                    |	          }|r|d         nd
}|                                }|r|d         nd
}dt          dd
ffd}|                                 t          d           t          d           	 t          |          }n># t          $ r1}t          d|            |                                 Y d
}~d
S d
}~ww xY wt          dt          |           d           d
}|r3t!          |          }|                                 t          d           d
g..fd}t%          j        t$          j        |           t%          j        t$          j        |           d
}d
}d
}|dk    }d}	 	 d
}t+          |j        o||j        o|||          }|.d<   	 |                                 n*# t          $ r}t          d|            Y d
}~n<d
}~ww xY w|                    |           t2          j                                         t9          j        d           t          d           t          d           t          d           t          d|            |j        rt          d           nt          d            t          d!           t          d           |j        rt?          ||          }ntA          ||          }|r,t          d"           |!                                 d
.d<   d
}ntE          |          } t          d#|             tG          ||| |	|
||$          }|$                    |           |%                                 |&                    |           |'                    |           |j         }!t2          j(        )                                }"|!rtU          j+        |"          nd
}#|!rtY          j-        |"           	 	 |j.        rn|/                    |          r3|0                                }$|$||$\  }%}&|1                    |%|&           |!rMte          j2        t2          j(        gg g d          d         r$t2          j(        3                    d%          }'|'d&v rn,n|4                    |          rnt9          j        d'           |# tU          j5        |"tT          j6        |#           n(# |#!tU          j5        |"tT          j6        |#           w w xY w|&                    d
           |j.        }(|7                    |( (          })d
}d
.d<   d
}|(rd)}*ntq                      }* ||)           |(rQ|?|9                    ||tu          |)          ||*          }+|;                    |+d)d+,           t          d-           nr|)}|Y|9                    ||tu          |)          ||*          }+|*|*nd.},|;                    |+|,d+,           |*rt          d/|*            t          d0|) d	           n# tx          $ rW t          d1           |+|j=        r$|7                    d+(          }) ||)           d
}n||!                                 d
}Y nmt          $ ra}t          d2|            |+|j=        r$|7                    d+(          }) ||)           d
}n||!                                 d
}Y d
}~nd
}~ww xY w||!                                 ||>                                 |D ]}-|-                                 |                                 n^# ||!                                 ||>                                 |D ]}-|-                                 |                                 w xY w| r"|r t          d3           t          || |           |r#t          d4|            t          d5           d
S t          d6           d
S )7u   Run teleoperation with continuous demonstration recording.

    Cameras and robots are fully instantiated and torn down each episode so
    every demonstration starts from a clean state.

    - Press the leader button (or Enter) to START each episode; press again
      to STOP and save.
    - During recording, each foot-pedal press logs a subtask boundary into
      ``event_markers`` (and into ``audio_segments`` when ``--record-audio``).
    - After each stop, mark the verdict on the keyboard: ``Enter`` for
      success, ``f`` for failure (30 s timeout → ``pending``).
    - Incomplete recordings (estop / Ctrl-C) are detected via the ``complete``
      flag in metadata.json and overridden on the next run.

    Args:
        record_audio: If True, capture continuous microphone audio in parallel
            with the recording. The first pedal press in an episode starts
            the stream; subsequent presses mark segment boundaries aligned
            with the corresponding ``event_markers``. Per-segment WAVs land
            under ``<recording_dir>/audio/`` with sidecar JSONs and an
            ``audio_segments`` index in ``metadata.json``.
        audio_device_index: Optional PyAudio input device index. Default:
            system default microphone.
    z=
============================================================z  DEMONSTRATION RECORDINGz=============================================================
rawTrN   z
  Task       : z  Instruction: rM  Nr  dest_dirr   c                     t                    }|                                r9t          j        || |j        z             t          d| |j        z              d S t          d|            d S )Nu   ✓ Calibration copied → z)  Warning: calibration file not found at )r   r0  r2  copyrS   rp   )ru  	calib_srcrn  s     r"   _copy_calibrationz(run_recording.<locals>._copy_calibration1  s    )**	 	KK	8in#<===K9>0IKKLLLLLIiIIJJJJJr9   z  Foot pedal: each press during recording logs a subtask boundary (into event_markers; into audio_segments when --record-audio).
zInitializing cameras...zError initialising cameras: u   ✓ z camera(s) ready
)device_indexz  Audio recording: stream opens at episode start; pre-first-press audio is discarded as warm-up noise.  From first press onwards, audio_full.wav + per-press segment WAVs land in <recording_dir>/audio/.
c                 P    d         d                                           d S d S )Nr   )emergency_stop)signumframe_active_ctrls     r"   r|  z%run_recording.<locals>.emergency_stopX  s1    ?&O**,,,,, '&r9   rj  )use_right_leaderuse_left_leaderuse_right_followeruse_left_followerr   zError initialising robots: g?z  READYz<============================================================z
  Data dir   : z/
  Press the button on any leader arm to START.z"
  Press Enter to START recording.z  Press 'q' to end session.
z
Ending session.
z
  Output: )r   r<   r=   r&   r'   r>   r?   r   rK  rO  rx   re  )
teacher_idtask_idraw_data_pathcamera_config_idcalibration_result_idF)statusr0   u*   
Recording aborted — marked as failure.
pendingz  Demonstration marked as: u   ✓ Recording saved to: z
Cancelled by user.z
Error during recording: z
Uploading to S3...u   
✓ Done.  Last recording at: z<  Run 'rd convert' to extract PNG frames from camera files.
u!   
✓ Done (no recordings saved).
)@rp   r  r   rg   r%  r   r   r   r1  snapshot_camera_configr   r  get_latest_calibration_resultr  closer}   r   start_sessionsignalSIGTERMSIGINTr   uses_leaderssetup_for_teleop_recordingsetupr  stdoutflushr   r   waits_for_button_startri  rb  rz   r9  r;   rj   rU   set_active_recordingdrain_pedal_eventsrQ  rR  rS  rT  rU  rV  rW  poll_subtaskr   mark_boundaryr[  r\  rZ  rX  rY  rv   rg  add_demonstrationr4   update_demonstrationKeyboardInterruptrC   stop_sessionrH  )/rk  rl  r>   rm  rn  ro  rp  rq  rr  r&   r'   r&  r  r  _f_cam_cfg_datar  task_recordr  latest_calibr  ry  r   r   r?   r|  last_saved_dirrecorderr<   	use_rightuse_leftquit_sessionr=   needs_stdinr_  r`  capturedr   r   ra  estop	saved_dirverdictdemo_idr  rc   r  s/       `                                         @r"   run_recordingr    sh   F 
/	
%&&&	/ #.--IH~~%	1HNN4$N///!!J	
)i
)
)***	
0,
0
0
0111 
BM$%% 	* IbMMM	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	*44]DVWW M M M44R9KLLM %%i00K#.8k$DG3355L2>HL..DKD KT K K K K K K NN		K   

#$$$*+=>>   0Q00111 

1W
1
1
1222 /3N 
&4FGGG$$&&&X	
 	
 	
 6:FL- - - - - M&..111
M&-000 &*N04H26
"IH|]	<H  /!*!7!EI ) 6 C8#,"*	      /LO ;;====   7A77888
 OO,--- JJsOOO /""")(OOO0h00111/ =HIIII;<<<1222/"""/ T67GSS67GSS +,,, ))+++"&Q#'  099M000111,!1+#!1#-  H OO,---$$&&& **+;<<<(()9::: (>>K!!##B4?I7,R000TL "b!!!K%'? 
 !--.>?? G#+#<#<#>#>#/N4N+3LE5*88FFF" "!=#)b"a@@C &!$!2!2B!%666 %$>>*:;; "!Jt$$$)%,  +%b'*;\JJJ  +%b'*;\JJJJ , **4000$<E !//U/CCIH"LO#  .)2+--i((( & 22#- '&))nn)9.C 3  G ++GIQV+WWWDEEE&N "..)#"%i..%5*? /   %,$7Y''%'PPP CAAABBB:Y:::;;;{]	<@  $ $ $$%%%H$9 ///??Ii(((#)%%'''# $ $ $.1..///H$9 ///??Ii(((#)%%'''#$ '%%'''%''))) 	 	CIIKKKK '%%'''%''))) 	 	CIIKKKK  ;^ ;$%%%^Y	::: 5AAABBBMNNNNN344444s   /D >CD C##D &C#'D  D$#D$*F: :
G5&G00G5$)] K# "] #
L
-L?] L

H] CW: #] :%XD"] a) A` a) "	`+A`a) `a) )Ac)Ar   r   rer[  r2  r  r  rS  rD   r   rU  dataclassesr   r   r   pathlibr   typingr   r   r	   r?  numpyr   raiden._clockr
   r   r   r!   raiden._configr   r   raiden.audior   raiden.camera_configr   raiden.camerasr   raiden.controlr   raiden.db.databaser   raiden.robot.controllerr   raiden.utilsr   r#   r%   r;   r4   r  compiler  r  tupler  r   r%  r9  rH  r7   rb  rg  ri  r  r8   r9   r"   <module>r     sV   8  				    



       



 ) ) ) ) ) ) ) )             ' ' ' ' ' ' ' ' ' '      7 7 7 7 7 7 ; ; ; ; ; ; : : : : : : : : & & & & & & - - - - - - ! ! ! ! ! ! * * * * * * % % % % % % 3 3 3 3 3 3 # # # # # #/DN //@ / / / /&        &z9 z9 z9 z9 z9 z9 z9 z9F %!- !-!-	&\!- !- !- !-R 
-..
S 
S4Z 
 
 
 
-U38_ - - - -:" " " " ">$ 4    F
@ 
@c 
@3 
@4 
@ 
@ 
@ 
@$?%?? 
? ? ? ?<!8C= ! ! ! !H?%?? 
? ? ? ?J ,,(,@5 @5}@5@5 @5 	@5
 @5 @5 @5 @5 !@5 
@5 @5 @5 @5 @5 @5r9   