This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

1#!/usr/bin/env bash 2# 3# Drives one fully unattended closed-loop experiment run: 4# 5# 1. Force-stops the sample app and clears Logcat so the cold start is 6# clean and the only ExperimentAuto: log lines are from this run. 7# 2. Picks a wall-clock target start_at_wall_ms = now + APP_BOOT_PAD seconds. 8# 3. Launches AppActivity via `am start` with the auto-mode intent extras 9# (model_name, start_at_wall_ms, duration_ms). The app's Compose 10# auto-driver selects the model, waits for the same wall-clock target, 11# runs the experiment buffer for duration_ms, writes the JSON, and 12# finishes the activity. 13# 4. Sleeps until start_at_wall_ms locally on the Mac, then plays the 14# video in QuickTime at the requested --start-offset (seconds). Both 15# sides hit the same wall-clock instant. 16# 5. Sleeps for the buffer duration, pauses QuickTime. 17# 6. Diff-polls /sdcard/.../experiment_logs for the new JSON file (with a 18# 10s timeout) and pulls it into experiments/<run-id>/. 19# 7. Captures the ExperimentAuto Logcat lines for the manifest, writes 20# experiments/<run-id>/manifest.json with t0_wall_ms, video offset, 21# device label, model tag, and the captured log lines. 22# 23# Usage: 24# tools/run_auto_experiment.sh \ 25# --video ~/Downloads/clip.mp4 \ 26# --duration 10 \ 27# --model-tag yolo11n_su_416 \ 28# [--start-offset 10] \ 29# [--device <serial>] 30# 31# After at least two runs (model A then model B against the same video, 32# same --start-offset) point tools/compare_logs.py at experiments/ to 33# build the comparison report. 34 35set -euo pipefail 36 37ADB="${ADB:-/Users/virtualintern/Library/Android/sdk/platform-tools/adb}" 38PKG="com.nate.posedetection.androidApp" 39ACTIVITY="$PKG/com.nate.posedetection.AppActivity" 40REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" 41EXPERIMENTS_DIR="$REPO_ROOT/experiments" 42REMOTE_LOGS_DIR="/sdcard/Android/data/$PKG/files/experiment_logs" 43APP_BOOT_PAD_SECONDS="${APP_BOOT_PAD_SECONDS:-9}" 44PULL_TIMEOUT_SECONDS="${PULL_TIMEOUT_SECONDS:-15}" 45 46usage() { 47 sed -n '3,33p' "$0" 48 exit "${1:-1}" 49} 50 51VIDEO="" 52DURATION="" 53MODEL_TAG="" 54START_OFFSET=0 55DEVICE="" 56 57while [ $# -gt 0 ]; do 58 case "$1" in 59 --video) VIDEO="$2"; shift 2 ;; 60 --duration) DURATION="$2"; shift 2 ;; 61 --model-tag) MODEL_TAG="$2"; shift 2 ;; 62 --start-offset) START_OFFSET="$2"; shift 2 ;; 63 --device) DEVICE="$2"; shift 2 ;; 64 -h|--help) usage 0 ;; 65 *) echo "unknown arg: $1" >&2; usage ;; 66 esac 67done 68 69[ -n "$VIDEO" ] || { echo "error: --video is required" >&2; exit 2; } 70[ -n "$DURATION" ] || { echo "error: --duration is required" >&2; exit 2; } 71[ -n "$MODEL_TAG" ] || { echo "error: --model-tag is required" >&2; exit 2; } 72 73if [ ! -f "$VIDEO" ]; then 74 echo "error: video file not found: $VIDEO" >&2 75 exit 2 76fi 77VIDEO_ABS="$(cd "$(dirname "$VIDEO")" && pwd)/$(basename "$VIDEO")" 78 79# Resolve device. 80if [ -z "$DEVICE" ]; then 81 CONNECTED="$("$ADB" devices | awk 'NR>1 && $2=="device" {print $1}')" 82 NUM_CONNECTED="$(printf '%s\n' "$CONNECTED" | grep -c . || true)" 83 if [ "$NUM_CONNECTED" -eq 0 ]; then 84 echo "error: no devices connected." >&2 85 exit 3 86 elif [ "$NUM_CONNECTED" -gt 1 ]; then 87 echo "error: multiple devices connected — pass --device <serial>." >&2 88 printf ' %s\n' $CONNECTED >&2 89 exit 3 90 fi 91 DEVICE="$CONNECTED" 92 echo "==> auto-selected device: $DEVICE" 93fi 94 95now_ms() { python3 -c 'import time; print(int(time.time()*1000))'; } 96 97DURATION_MS=$(python3 -c "print(int(float('$DURATION') * 1000))") 98 99echo "==> auto experiment" 100echo " model_tag = $MODEL_TAG" 101echo " duration = ${DURATION}s ($DURATION_MS ms)" 102echo " video = $VIDEO_ABS" 103echo " start_offset = ${START_OFFSET}s" 104 105# 1. Snapshot the existing logs so we can diff-pull only the new file. 106LOGS_BEFORE="$("$ADB" -s "$DEVICE" shell "ls -1 $REMOTE_LOGS_DIR 2>/dev/null" \ 107 | tr -d '\r' | grep '\.json$' || true)" 108EXISTING_COUNT=$(printf '%s\n' "$LOGS_BEFORE" | grep -c '\.json$' || true) 109echo "==> $EXISTING_COUNT existing log(s) on device (will be ignored)" 110 111# 2. Pre-open the video in QuickTime *first* and position to start_offset 112# while paused. Large 4K files can take 10-60s to index, so we do this 113# before any wall-clock timing matters. The 600s timeout covers extreme 114# cases (default AppleEvent timeout is 60s and can fire on big files). 115echo "==> pre-opening video in QuickTime (large files may take a moment)..." 116osascript <<APPLESCRIPT 117with timeout of 600 seconds 118 tell application "QuickTime Player" 119 activate 120 if (count of documents) > 0 then 121 try 122 close every document saving no 123 end try 124 end if 125 open POSIX file "$VIDEO_ABS" 126 repeat while (count of documents) is 0 127 delay 0.1 128 end repeat 129 tell front document 130 pause 131 set current time to $START_OFFSET 132 present 133 end tell 134 end tell 135end timeout 136APPLESCRIPT 137 138# 3. Cold-start the app: kill any existing instance and clear Logcat. 139echo "==> force-stopping app and clearing Logcat..." 140"$ADB" -s "$DEVICE" shell am force-stop "$PKG" >/dev/null 141"$ADB" -s "$DEVICE" logcat -c >/dev/null 142 143# 4. Compute the wall-clock target and launch via intent with auto-mode extras. 144NOW_MS=$(now_ms) 145START_AT_WALL_MS=$((NOW_MS + APP_BOOT_PAD_SECONDS * 1000)) 146RUN_ID="${START_AT_WALL_MS}_$(printf '%s' "$MODEL_TAG" | tr -c 'A-Za-z0-9._-' '_' | sed 's/^_*//; s/_*$//')" 147RUN_DIR="$EXPERIMENTS_DIR/$RUN_ID" 148echo " start_at_wall = $START_AT_WALL_MS (in ${APP_BOOT_PAD_SECONDS}s)" 149echo " run_dir = $RUN_DIR" 150 151echo "==> launching $ACTIVITY with auto-mode extras..." 152"$ADB" -s "$DEVICE" shell am start -n "$ACTIVITY" \ 153 --ez experiment_auto true \ 154 --es model_name "$MODEL_TAG" \ 155 --el start_at_wall_ms "$START_AT_WALL_MS" \ 156 --el duration_ms "$DURATION_MS" >/dev/null 157 158# 5. Wait until the wall-clock target, then play (already pre-loaded so this 159# is sub-second). t0_wall_ms is captured immediately before the play call. 160WAIT_MS=$((START_AT_WALL_MS - $(now_ms))) 161if [ "$WAIT_MS" -gt 0 ]; then 162 python3 -c "import time; time.sleep($WAIT_MS/1000.0)" 163fi 164 165T0_WALL_MS=$(now_ms) 166echo "==> playing video at offset ${START_OFFSET}s (t0_wall_ms=$T0_WALL_MS)" 167osascript -e 'tell application "QuickTime Player" to play front document' >/dev/null 168 169# 5. Sleep the buffer duration plus a small safety margin then pause. 170echo "==> sleeping ${DURATION}s for buffer to fill..." 171sleep "$DURATION" 172osascript -e 'tell application "QuickTime Player" to pause front document' >/dev/null 173 174# 6. Poll for the new log file with a timeout. 175echo "==> polling for new log file (timeout ${PULL_TIMEOUT_SECONDS}s)..." 176NEW_LOGS="" 177DEADLINE=$(( $(now_ms) + PULL_TIMEOUT_SECONDS * 1000 )) 178while [ "$(now_ms)" -lt "$DEADLINE" ]; do 179 LOGS_AFTER="$("$ADB" -s "$DEVICE" shell "ls -1 $REMOTE_LOGS_DIR 2>/dev/null" \ 180 | tr -d '\r' | grep '\.json$' || true)" 181 NEW_LOGS="$(comm -13 \ 182 <(printf '%s\n' "$LOGS_BEFORE" | sort -u) \ 183 <(printf '%s\n' "$LOGS_AFTER" | sort -u) \ 184 | grep '\.json$' || true)" 185 if [ -n "$NEW_LOGS" ]; then 186 break 187 fi 188 sleep 0.25 189done 190 191mkdir -p "$RUN_DIR" 192 193if [ -z "$NEW_LOGS" ]; then 194 echo "error: no new log file appeared on device after ${PULL_TIMEOUT_SECONDS}s" >&2 195 echo " recent ExperimentAuto Logcat:" >&2 196 "$ADB" -s "$DEVICE" logcat -d -s ExperimentAuto:I 2>/dev/null | tail -30 >&2 || true 197 exit 4 198fi 199 200echo "==> pulling new log(s) into $RUN_DIR/" 201while IFS= read -r f; do 202 [ -z "$f" ] && continue 203 "$ADB" -s "$DEVICE" pull "$REMOTE_LOGS_DIR/$f" "$RUN_DIR/" >/dev/null 204 echo " pulled $f" 205done <<< "$NEW_LOGS" 206 207# 7. Capture the ExperimentAuto log lines (for the manifest). 208LOGCAT_EXCERPT="$("$ADB" -s "$DEVICE" logcat -d -s ExperimentAuto:I 2>/dev/null \ 209 | grep ExperimentAuto || true)" 210 211DEVICE_MANUFACTURER="$("$ADB" -s "$DEVICE" shell getprop ro.product.manufacturer 2>/dev/null | tr -d '\r' || true)" 212DEVICE_MODEL_NAME="$("$ADB" -s "$DEVICE" shell getprop ro.product.model 2>/dev/null | tr -d '\r' || true)" 213DEVICE_LABEL="$DEVICE_MANUFACTURER $DEVICE_MODEL_NAME" 214 215NEW_LOGS_JOINED="$(printf '%s\n' "$NEW_LOGS" | tr '\n' ',' | sed 's/,$//')" 216 217RUN_ID="$RUN_ID" \ 218MODEL_TAG="$MODEL_TAG" \ 219VIDEO_ABS="$VIDEO_ABS" \ 220START_OFFSET="$START_OFFSET" \ 221DURATION="$DURATION" \ 222T0_WALL_MS="$T0_WALL_MS" \ 223START_AT_WALL_MS="$START_AT_WALL_MS" \ 224DEVICE="$DEVICE" \ 225DEVICE_LABEL="$DEVICE_LABEL" \ 226NEW_LOGS_JOINED="$NEW_LOGS_JOINED" \ 227LOGCAT_EXCERPT="$LOGCAT_EXCERPT" \ 228python3 -c ' 229import json, os 230files = [f for f in os.environ["NEW_LOGS_JOINED"].split(",") if f] 231logcat = [l for l in os.environ["LOGCAT_EXCERPT"].splitlines() if l.strip()] 232manifest = { 233 "run_id": os.environ["RUN_ID"], 234 "mode": "auto", 235 "model_tag": os.environ["MODEL_TAG"], 236 "video_path": os.environ["VIDEO_ABS"], 237 "video_start_offset_seconds": float(os.environ["START_OFFSET"]), 238 "duration_seconds": float(os.environ["DURATION"]), 239 "t0_wall_ms": int(os.environ["T0_WALL_MS"]), 240 "start_at_wall_ms": int(os.environ["START_AT_WALL_MS"]), 241 "device_serial": os.environ["DEVICE"], 242 "device_label": os.environ["DEVICE_LABEL"].strip(), 243 "log_files": files, 244 "logcat_excerpt": logcat, 245} 246print(json.dumps(manifest, indent=2)) 247' > "$RUN_DIR/manifest.json" 248 249echo "==> wrote $RUN_DIR/manifest.json" 250echo 251echo "Run dir: $RUN_DIR" 252ls -la "$RUN_DIR"