Files
Sylpheed/tools/re-capture/wait_screen.sh
Sylpheed RE agent 06d00d4529 tools: launch_mission reaches Stage 02 flight unattended again - wait for screens, not seconds
Two separate reasons the scripted route stopped, both measured rather than guessed:

* LOAD -> READY ROOM is not 28 s. Both runs on 2026-08-23 overran it, so the next
  press was eaten by the transition and the run ended up in OPTIONS once and
  BRIEFINGS once. wait_screen.sh now waits for the screen, with an optional
  --tap that clears a dialog the caller cannot know about (a freshly restored
  profile inserts "Auto-Save is active. OK?" here).
* The READY ROOM is DRAWN long before it is USABLE: it comes up with a
  "Preparing to Sortie" spinner and TAKE OFF greyed out. The two states differ by
  1.7 units of blue whole-image, so screen_id.py cannot separate them and should
  not try. take_off_armed.py tests the label instead: 0.0000 bright pixels while
  preparing, 0.1633 once armed, on three captures from two runs. It carries its
  own position check - the always-enabled BRIEFINGS label below reads 0.1027 in
  all three, to four decimals, so if that reference is dark the boxes are off the
  labels and the answer is "unknown", not a confident wrong one.

screen_id.py gains a "readyroom" class from the same measurements; nothing else
reclassifies.

Verified end to end and unattended: boot -> title -> LOAD GAME -> slot 01 ->
READY ROOM -> TAKE OFF -> "IN FLIGHT at 34s", pilot bound and engaging.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PMRJjbxLqZtsb5Vb7KunPE
2026-08-23 18:02:12 +00:00

58 lines
2.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# Wait until screen_id.py reports <class>, instead of sleeping a guessed number
# of seconds.
#
# Written because `launch_mission.sh` allowed a fixed 28 s for LOAD -> READY
# ROOM and neither run on 2026-08-23 was ready in time: the next press was
# swallowed by the transition, which left one run in OPTIONS and the next in
# BRIEFINGS, and `wait_flight.sh` then tapped A into the wrong screen for five
# minutes. See docs/re/dynamic-re-state-restore.md.
#
# `--tap BTN` presses BTN every 6 s *until the class is first seen* — that is
# what clears a dialog the caller cannot know about, such as the
# "Auto-Save is active. OK?" that a freshly restored profile puts up. Tapping
# STOPS at the first match, because on most screens the button that dismisses a
# dialog also leaves the screen you are waiting for.
#
# Two consecutive matches are required, so a single frame caught mid-fade does
# not count as arrival.
#
# Usage: wait_screen.sh <class> [timeout_s] [--tap BTN]
set -u
CLASS="${1:?usage: wait_screen.sh <class> [timeout_s] [--tap BTN]}"
TIMEOUT="${2:-180}"
TAP=""
[ "${3:-}" = "--tap" ] && TAP="${4:-A}"
export HOME=/sylph-home/re
DISP="${DISPLAY:-:98}"
SD="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Same liveness rule as skip_intro.sh/wait_flight.sh: a failed screenshot must
# not be indistinguishable from "the screen is not there yet".
alive(){ ps -o pid=,stat= -C xenia_canary 2>/dev/null | awk '$2 !~ /^Z/ {print $1}'; }
DEADLINE=$(( SECONDS + TIMEOUT ))
hits=0; seen=0; last_tap=0
while [ $SECONDS -lt $DEADLINE ]; do
rm -f /tmp/ws.png
if ! screenshot /tmp/ws.png >/dev/null 2>&1 || [ ! -s /tmp/ws.png ]; then
xdpyinfo -display "$DISP" >/dev/null 2>&1 \
|| { echo "DISPLAY LOST at ${SECONDS}s"; exit 3; }
sleep 2; continue
fi
[ -n "$(alive)" ] || { echo "EMULATOR GONE at ${SECONDS}s"; exit 4; }
got="$(python3 "$SD/screen_id.py" /tmp/ws.png | awk '{print $1}')"
if [ "$got" = "$CLASS" ]; then
seen=1; hits=$(( hits + 1 ))
[ $hits -ge 2 ] && { echo "$CLASS at ${SECONDS}s"; exit 0; }
else
hits=0
if [ -n "$TAP" ] && [ $seen -eq 0 ] && [ $(( SECONDS - last_tap )) -ge 6 ]; then
python3 "$SD/pad.py" tap "$TAP" 0.3
last_tap=$SECONDS
fi
fi
sleep 2
done
echo "NO $CLASS within ${TIMEOUT}s (last seen: ${got:-none})"; exit 1