#!/usr/bin/env python3
"""DF-5720 — scripted driver for the guest's socket-backed serial console.

One persistent connection for the whole ddb session (QEMU applies backpressure
to the guest UART once the chardev client buffer fills, so we drain
continuously). Lines are sent in <=8-byte chunks with pacing: the guest's UART
RX FIFO is 16 bytes and ddb polls cngetc without flow control.

Sequence (see run.sh): enter ddb via sysctl over ssh, then at db> run the
modifier test battery, then continue.
"""
import os, socket, sys, threading, time

SOCK = "/tmp/opencode/df-5720-ser.sock"
DRAIN = "/tmp/opencode/df-5720-drain.log"   # socket-side copy of guest output

def connect():
    while True:
        try:
            s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
            s.connect(SOCK)
            return s
        except (FileNotFoundError, ConnectionRefusedError) as e:
            print(f"connect failed: {e}; retrying...", flush=True)
            time.sleep(2)

_drained = threading.Event()
def drainer(s):
    with open(DRAIN, "ab", buffering=0) as d:
        _drained.set()
        while True:
            try:
                b = s.recv(65536)
            except OSError:
                return
            if not b:
                return
            d.write(b)

def send_line(s, line):
    data = (line if line.endswith("\n") else line + "\n").encode()
    for i in range(0, len(data), 8):
        s.sendall(data[i:i+8])
        time.sleep(0.15)

# (command, seconds to wait for output after sending)
SESSION = [
    ("x/x db_active",        2),   # control: letter-modifier hex dump
    ("x/4 db_active",        2),   # numeric modifier on 'x' -> silently "x"
    ("examine/4 db_active",  4),   # numeric modifier -> modif="examine" (!)
    ("print 0x41",           2),   # control: default 'x' format
    ("print/4 0x41",         2),   # numeric modifier -> modif="print" -> 'p'
    ("print/x 0x41",         2),   # control: explicit letter modifier
    ("write/4 db_active 1",  3),   # numeric modifier -> modif="write" -> error
    ("write/l db_active 1",  3),   # control: letter modifier write works
    ("x/x db_active",        2),   # post-write check (value unchanged: 1)
    ("c",                    6),   # continue out of ddb
]

def main():
    s = connect()
    t = threading.Thread(target=drainer, args=(s,), daemon=True)
    t.start(); _drained.wait()
    print("connected; drain ->", DRAIN, flush=True)
    time.sleep(2)  # capture any console output already flowing
    for cmd, wait in SESSION:
        print(f">>> {cmd}", flush=True)
        send_line(s, cmd)
        time.sleep(wait)
    print("session script complete", flush=True)
    time.sleep(2)

if __name__ == "__main__":
    main()
