client: debugging web cam
This commit is contained in:
parent
5be0837f45
commit
c274e8ce18
107
scripts/manual/eval_lesavka.sh
Executable file
107
scripts/manual/eval_lesavka.sh
Executable file
@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env bash
|
||||
# scripts/manual/eval_lesavka.sh - iterative health check for lesavka client/server/gadget
|
||||
# - Locally: probes TCP + gRPC handshake on LESAVKA_SERVER_ADDR
|
||||
# - Optional: if TETHYS_HOST is set, ssh to run lsusb + dmesg tail (enumeration check)
|
||||
# - Optional: if THEIA_HOST is set, ssh to show core/server status + hidg/uvc presence
|
||||
#
|
||||
# Env:
|
||||
# LESAVKA_SERVER_ADDR (default http://38.28.125.112:50051)
|
||||
# ITER=0 (loop forever) or number of iterations
|
||||
# SLEEP=10 (seconds between iterations)
|
||||
# TETHYS_HOST=host (ssh target for target machine; requires key auth)
|
||||
# THEIA_HOST=host (ssh target for server/gadget Pi)
|
||||
# SSH_OPTS="-o ConnectTimeout=5" (optional extra ssh flags)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SERVER=${LESAVKA_SERVER_ADDR:-http://38.28.125.112:50051}
|
||||
# default to a few iterations instead of infinite to avoid unintentional long runs
|
||||
ITER=${ITER:-5}
|
||||
SLEEP=${SLEEP:-10}
|
||||
SSH_OPTS=${SSH_OPTS:-"-o ConnectTimeout=5 -o BatchMode=yes"}
|
||||
SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
|
||||
PROTO_DIR="${PROTO_DIR:-${SCRIPT_DIR}/../../common/proto}"
|
||||
|
||||
hostport=${SERVER#http://}
|
||||
hostport=${hostport#https://}
|
||||
host=${hostport%%:*}
|
||||
port=${hostport##*:}
|
||||
|
||||
has_nc() { command -v nc >/dev/null 2>&1; }
|
||||
has_grpc() { command -v grpcurl >/dev/null 2>&1; }
|
||||
|
||||
probe_server() {
|
||||
echo "==> [local] $(date -Is) probing $SERVER"
|
||||
if has_nc; then
|
||||
if nc -zw3 "$host" "$port"; then
|
||||
echo " tcp: OK (port reachable)"
|
||||
else
|
||||
echo " tcp: FAIL (port unreachable)"
|
||||
fi
|
||||
else
|
||||
echo " tcp: skipped (nc not present)"
|
||||
fi
|
||||
|
||||
if has_grpc; then
|
||||
if [[ -f "${PROTO_DIR}/lesavka.proto" ]]; then
|
||||
if out=$(grpcurl -plaintext -max-time 5 \
|
||||
-import-path "${PROTO_DIR}" -proto lesavka.proto \
|
||||
"$host:$port" lesavka.Handshake/GetCapabilities 2>&1); then
|
||||
echo " gRPC Handshake (proto): OK → $out"
|
||||
else
|
||||
echo " gRPC Handshake (proto): FAIL → $out"
|
||||
fi
|
||||
else
|
||||
if out=$(grpcurl -plaintext -max-time 5 "$host:$port" list 2>&1); then
|
||||
echo " gRPC list (reflection): $out"
|
||||
else
|
||||
echo " gRPC list (reflection) FAIL → $out"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo " gRPC: skipped (grpcurl not present)"
|
||||
fi
|
||||
}
|
||||
|
||||
probe_tethys() {
|
||||
[[ -z "${TETHYS_HOST:-}" ]] && return
|
||||
echo "==> [tethys] $(date -Is) checking lsusb + dmesg tail on $TETHYS_HOST"
|
||||
ssh $SSH_OPTS "$TETHYS_HOST" '
|
||||
lsusb;
|
||||
echo "--- /dev/hidraw* ---";
|
||||
ls /dev/hidraw* 2>/dev/null || true;
|
||||
echo "--- dmesg (USB tail) ---";
|
||||
dmesg | tail -n 20
|
||||
' || echo " ssh to $TETHYS_HOST failed"
|
||||
}
|
||||
|
||||
probe_theia() {
|
||||
[[ -z "${THEIA_HOST:-}" ]] && return
|
||||
echo "==> [theia] $(date -Is) checking services/device nodes on $THEIA_HOST"
|
||||
ssh $SSH_OPTS "$THEIA_HOST" '
|
||||
systemctl --no-pager --quiet is-active lesavka-core && echo "lesavka-core: active" || echo "lesavka-core: INACTIVE";
|
||||
systemctl --no-pager --quiet is-active lesavka-server && echo "lesavka-server: active" || echo "lesavka-server: INACTIVE";
|
||||
echo "--- hidg nodes ---";
|
||||
ls -l /dev/hidg0 /dev/hidg1 2>/dev/null || true;
|
||||
echo "--- video nodes ---";
|
||||
ls -l /dev/video* 2>/dev/null | head;
|
||||
echo "--- recent server log ---";
|
||||
journalctl -u lesavka-server -n 20 --no-pager
|
||||
' || echo " ssh to $THEIA_HOST failed"
|
||||
}
|
||||
|
||||
count=0
|
||||
while :; do
|
||||
probe_server
|
||||
probe_theia
|
||||
probe_tethys
|
||||
|
||||
count=$((count + 1))
|
||||
if [[ "$ITER" -gt 0 && "$count" -ge "$ITER" ]]; then
|
||||
break
|
||||
fi
|
||||
echo "==> sleeping ${SLEEP}s (iteration $count complete)"
|
||||
sleep "$SLEEP"
|
||||
done
|
||||
|
||||
echo "Done."
|
||||
56
scripts/manual/kde-start-tethys.sh
Executable file
56
scripts/manual/kde-start-tethys.sh
Executable file
@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
# scripts/manual/kde-start-tethys.sh
|
||||
#
|
||||
# Start/restart SDDM on tethys and set display geometry over :0.
|
||||
# Intended for remote use after SSH-ing into tethys.
|
||||
#
|
||||
# Env overrides:
|
||||
# MODE=1920x1080 (preferred mode)
|
||||
# RATE=60 (refresh rate)
|
||||
# OUTPUTS="HDMI-1 DP-1" (space-separated outputs to try)
|
||||
# DISPLAY=:0 (X display; default :0)
|
||||
# XAUTHORITY=... (override cookie; otherwise auto-detected from SDDM)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
MODE=${MODE:-1920x1080}
|
||||
RATE=${RATE:-60}
|
||||
OUTPUTS=${OUTPUTS:-"HDMI-1 DP-1"}
|
||||
DISPLAY=${DISPLAY:-:0}
|
||||
|
||||
log() { printf "[kde-start] %s\n" "$*"; }
|
||||
|
||||
log "restarting sddm.service"
|
||||
sudo systemctl restart sddm
|
||||
sleep 2
|
||||
|
||||
# find SDDM Xauthority if not provided
|
||||
if [[ -z "${XAUTHORITY:-}" ]]; then
|
||||
XAUTHORITY=$(ls /var/run/sddm/*/xauth_* 2>/dev/null | head -n1 || true)
|
||||
fi
|
||||
|
||||
if [[ -z "${XAUTHORITY:-}" ]]; then
|
||||
log "warning: no XAUTHORITY found; xrandr may fail"
|
||||
fi
|
||||
|
||||
# wait for X to come up
|
||||
for attempt in {1..15}; do
|
||||
if DISPLAY=$DISPLAY XAUTHORITY=${XAUTHORITY:-} xrandr --query >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
log "setting mode ${MODE}@${RATE} on outputs: ${OUTPUTS}"
|
||||
for out in $OUTPUTS; do
|
||||
if DISPLAY=$DISPLAY XAUTHORITY=${XAUTHORITY:-} xrandr --output "$out" --mode "$MODE" --rate "$RATE" --primary >/dev/null 2>&1; then
|
||||
log "set $out to ${MODE}@${RATE}"
|
||||
else
|
||||
log "skip $out (xrandr failed)"
|
||||
fi
|
||||
done
|
||||
|
||||
log "current xrandr:"
|
||||
DISPLAY=$DISPLAY XAUTHORITY=${XAUTHORITY:-} xrandr --query || true
|
||||
|
||||
log "done."
|
||||
@ -1,10 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
# scripts/manual/usb-reset.sh
|
||||
# scripts/manual/usb-reset.sh - trigger USB reset RPC on the server
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"
|
||||
PROTO_DIR="${SCRIPT_DIR}/../../common/proto"
|
||||
|
||||
grpcurl \
|
||||
-plaintext \
|
||||
-import-path ./../../common/proto \
|
||||
-import-path "${PROTO_DIR}" \
|
||||
-proto lesavka.proto \
|
||||
-d '{}' \
|
||||
64.25.10.31:50051 \
|
||||
38.28.125.112:50051 \
|
||||
lesavka.Relay/ResetUsb
|
||||
|
||||
@ -89,6 +89,37 @@ fn next_minute() -> SystemTime {
|
||||
UNIX_EPOCH + Duration::from_secs(next)
|
||||
}
|
||||
|
||||
/// Pick the UVC gadget video node.
|
||||
/// Priority: 1) `LESAVKA_UVC_DEV` override; 2) first `video_output` node.
|
||||
/// Returns an error when nothing matches instead of guessing a capture card.
|
||||
fn pick_uvc_device() -> anyhow::Result<String> {
|
||||
if let Ok(path) = std::env::var("LESAVKA_UVC_DEV") {
|
||||
return Ok(path);
|
||||
}
|
||||
|
||||
// walk /dev/video* via udev and look for an output‑capable node (gadget exposes one)
|
||||
if let Ok(mut en) = udev::Enumerator::new() {
|
||||
let _ = en.match_subsystem("video4linux");
|
||||
if let Ok(devs) = en.scan_devices() {
|
||||
for dev in devs {
|
||||
let caps = dev
|
||||
.property_value("ID_V4L_CAPABILITIES")
|
||||
.and_then(|v| v.to_str())
|
||||
.unwrap_or_default();
|
||||
if caps.contains(":video_output:") {
|
||||
if let Some(node) = dev.devnode() {
|
||||
return Ok(node.to_string_lossy().into_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(anyhow::anyhow!(
|
||||
"no video_output v4l2 node found; set LESAVKA_UVC_DEV"
|
||||
))
|
||||
}
|
||||
|
||||
/*──────────────── Handler ───────────────────*/
|
||||
struct Handler {
|
||||
kb: Arc<Mutex<tokio::fs::File>>,
|
||||
@ -186,7 +217,9 @@ impl Relay for Handler {
|
||||
req: Request<tonic::Streaming<AudioPacket>>,
|
||||
) -> Result<Response<Self::StreamMicrophoneStream>, Status> {
|
||||
// 1 ─ build once, early
|
||||
let mut sink = audio::Voice::new("hw:UAC2Gadget,0")
|
||||
let uac_dev = std::env::var("LESAVKA_UAC_DEV").unwrap_or_else(|_| "hw:UAC2Gadget,0".into());
|
||||
info!(%uac_dev, "🎤 stream_microphone using UAC sink");
|
||||
let mut sink = audio::Voice::new(&uac_dev)
|
||||
.await
|
||||
.map_err(|e| Status::internal(format!("{e:#}")))?;
|
||||
|
||||
@ -218,7 +251,8 @@ impl Relay for Handler {
|
||||
req: Request<tonic::Streaming<VideoPacket>>,
|
||||
) -> Result<Response<Self::StreamCameraStream>, Status> {
|
||||
// map gRPC camera id → UVC device
|
||||
let uvc = std::env::var("LESAVKA_UVC_DEV").unwrap_or_else(|_| "/dev/video4".into());
|
||||
let uvc = pick_uvc_device().map_err(|e| Status::internal(format!("{e:#}")))?;
|
||||
info!(%uvc, "🎥 stream_camera using UVC sink");
|
||||
|
||||
// build once
|
||||
let relay =
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user