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
|
#!/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 \
|
grpcurl \
|
||||||
-plaintext \
|
-plaintext \
|
||||||
-import-path ./../../common/proto \
|
-import-path "${PROTO_DIR}" \
|
||||||
-proto lesavka.proto \
|
-proto lesavka.proto \
|
||||||
-d '{}' \
|
-d '{}' \
|
||||||
64.25.10.31:50051 \
|
38.28.125.112:50051 \
|
||||||
lesavka.Relay/ResetUsb
|
lesavka.Relay/ResetUsb
|
||||||
|
|||||||
@ -89,6 +89,37 @@ fn next_minute() -> SystemTime {
|
|||||||
UNIX_EPOCH + Duration::from_secs(next)
|
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 ───────────────────*/
|
/*──────────────── Handler ───────────────────*/
|
||||||
struct Handler {
|
struct Handler {
|
||||||
kb: Arc<Mutex<tokio::fs::File>>,
|
kb: Arc<Mutex<tokio::fs::File>>,
|
||||||
@ -186,7 +217,9 @@ impl Relay for Handler {
|
|||||||
req: Request<tonic::Streaming<AudioPacket>>,
|
req: Request<tonic::Streaming<AudioPacket>>,
|
||||||
) -> Result<Response<Self::StreamMicrophoneStream>, Status> {
|
) -> Result<Response<Self::StreamMicrophoneStream>, Status> {
|
||||||
// 1 ─ build once, early
|
// 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
|
.await
|
||||||
.map_err(|e| Status::internal(format!("{e:#}")))?;
|
.map_err(|e| Status::internal(format!("{e:#}")))?;
|
||||||
|
|
||||||
@ -218,7 +251,8 @@ impl Relay for Handler {
|
|||||||
req: Request<tonic::Streaming<VideoPacket>>,
|
req: Request<tonic::Streaming<VideoPacket>>,
|
||||||
) -> Result<Response<Self::StreamCameraStream>, Status> {
|
) -> Result<Response<Self::StreamCameraStream>, Status> {
|
||||||
// map gRPC camera id → UVC device
|
// 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
|
// build once
|
||||||
let relay =
|
let relay =
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user