Files
bf-infra/docs/tp6-monitor-core.sh
Christoph Emsenhuber 6cd41d5e9b TP25: Gehweg-Messung -- was eine WLAN-Uebergabe wirklich kostet
Die "~4 Sekunden je Wechsel" aus der Nachtmessung waren als Obergrenze markiert,
und die Vorsicht war berechtigt: 5 Pings/s auf das Telefon zeigen bei -48 bis
-55 dBm Uebergabekosten von 0 bis 2,6 s, meist unter einer halben Sekunde. Fast
Transition funktioniert. Der Zaehlerstand nach einer Neuanmeldung hatte die
Luecke vorgetaeuscht.

Der wahre Befund ist schlimmer und liegt woanders: bei -69 bis -77 dBm ist das
Telefon 6 bis 45 s nicht erreichbar, zweimal ueber 20 s am Stueck. Die Trennlinie
laeuft sauber entlang der Signalstaerke, nicht entlang der Wechsel -- es ist kein
Roaming-Problem, es ist ein Dead Spot.

Ehrlich dazu: ueber den ganzen Weg gingen 44 % der Pings verloren, und das ist
nicht alles Funk -- ein iPhone mit gesperrtem Display beantwortet ICMP verzoegert
oder gar nicht (RTT-Median 3,7 ms, Maximum 8,2 s). Power-Save haengt aber am
Display, nicht am Ort; dass alle langen Luecken in der schwachen Zone liegen und
alle kurzen in der starken, erklaert es nicht. Fuer eine exakte Zahl braucht es
ein waches Telefon.

tp6-monitor-core.sh bekommt eine Reconnect-Schleife: die SSH-Sitzung ist mitten
in der Messung weggebrochen und die Funkdaten endeten 2,5 Minuten vor dem
Ping-Mitschnitt -- ausgerechnet im interessanten Teil.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 15:15:56 +02:00

96 lines
4.1 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# TP6 — Core-Sampler (read-only)
#
# Haengt EINE persistente SSH-Sitzung an crs326-core und laesst dort eine Console-Schleife
# laufen, die im festen Takt eine CSV-Zeile ausgibt: Core-CPU, WAN-Rate auf vlan100 und
# — optional — die Registration-Table-Werte EINES WLAN-Clients.
#
# 🔴 Ausschliesslich Lesezugriffe (get / find / monitor-traffic once). Kein Write, kein
# /system script, kein /system logging. Aendert nichts am Netz.
#
# 🔴 Unter CAPsMAN fuehrt der MANAGER die Registration-Table — deshalb wird der CORE
# abgefragt und nicht ap1/ap2, wo die Tabelle leer waere.
#
# Aufruf:
# bash docs/tp6-monitor-core.sh --out /pfad/sampler.csv # nur CPU/WAN
# bash docs/tp6-monitor-core.sh --out /pfad/sampler.csv --mac F2:62:4C:E1:AF:09
#
# Beenden: Ctrl-C (oder kill auf die PID).
set -uo pipefail
MAC=""
INTERVAL=2
OUT=""
ROUTER_IP="10.40.99.1"
while [[ $# -gt 0 ]]; do
case "$1" in
--mac) MAC="${2:-}"; shift 2 ;;
--interval) INTERVAL="${2:-2}"; shift 2 ;;
--out) OUT="${2:-}"; shift 2 ;;
--ip) ROUTER_IP="${2:-}"; shift 2 ;;
*) echo "unbekanntes Argument: $1" >&2; exit 2 ;;
esac
done
[[ -n "$OUT" ]] || { echo "--out fehlt" >&2; exit 2; }
# Passwort aus dem Credential-Store. Wird NIE ausgegeben.
PW="$(grep -m1 '^CRS326_CORE_PASS=' ~/.secrets/credentials.env | cut -d= -f2- | sed 's/^["'"'"']//; s/["'"'"']$//')"
[[ -n "$PW" ]] || { echo "CRS326_CORE_PASS nicht in ~/.secrets/credentials.env gefunden" >&2; exit 1; }
# RouterOS-Schleife. Platzhalter werden unten ersetzt (RouterOS nutzt $ selbst, daher kein
# Shell-Interpolieren im Schleifenkoerper).
read -r -d '' ROS <<'ROS_EOF'
:local m "__MAC__";
:while (true) do={
:local cpu [/system resource get cpu-load];
:local w [/interface monitor-traffic vlan100 once as-value];
:local line ([/system clock get time] . "," . $cpu . "," . ($w->"rx-bits-per-second") . "," . ($w->"tx-bits-per-second"));
:if ([:len $m] > 0) do={
:local id [/interface/wifi/registration-table find where mac-address=$m];
:if ([:len $id] = 0) do={
:set line ($line . ",absent,,,,,,");
} else={
:local r [/interface/wifi/registration-table get [:pick $id 0]];
:set line ($line . "," . ($r->"interface") . "," . ($r->"signal") . "," . ($r->"tx-rate") . "," . ($r->"rx-rate") . "," . ($r->"tx-bits-per-second") . "," . ($r->"rx-bits-per-second") . "," . ($r->"uptime") . "," . ($r->"last-activity"));
};
};
:put $line;
:delay __DELAY__s;
}
ROS_EOF
ROS="${ROS//__MAC__/$MAC}"
ROS="${ROS//__DELAY__/$INTERVAL}"
# 🔴 Die RouterOS-Console parst eine per SSH uebergebene Mehrzeilen-Eingabe ZEILENWEISE
# ("expected closing brace"). Das Skript muss deshalb als EINE Zeile ankommen —
# und jeder do={}-Block braucht dann ein abschliessendes ";", sonst kommt
# "expected end of command".
ROS="$(printf '%s' "$ROS" | tr '\n' ' ')"
echo "ts_epoch,ts_local,ros_time,cpu,wan_rx_bps,wan_tx_bps,sta_iface,sta_signal,sta_tx_rate,sta_rx_rate,sta_tx_bps,sta_rx_bps,sta_uptime,sta_last_activity" > "$OUT"
echo "Sampler laeuft -> $OUT (Takt ${INTERVAL}s${MAC:+, Client $MAC})"
# Lokaler Zeitstempel beim Eintreffen der Zeile. Bewusst kein awk: das BSD-awk auf macOS
# kennt systime()/strftime() nicht (das ist gawk).
#
# 🔴 Mit Reconnect-Schleife: die SSH-Sitzung ist am 2026-08-21 mitten in einer Gehweg-Messung
# weggebrochen ("Can't assign requested address"), und die Funkdaten endeten 2,5 Minuten vor
# dem Ping-Mitschnitt — ausgerechnet im interessanten Teil. Ein Sampler, der still stirbt,
# ist schlimmer als keiner.
while true; do
sshpass -p "$PW" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=8 \
-o ServerAliveInterval=15 -o ServerAliveCountMax=3 "admin@${ROUTER_IP}" "$ROS" \
2>>"${OUT%.csv}.stderr.log" \
| while IFS= read -r line; do
printf '%s,%s,%s\n' "$(date +%s)" "$(date +%H:%M:%S)" "$line" >> "$OUT"
done
printf '%s,%s,RECONNECT\n' "$(date +%s)" "$(date +%H:%M:%S)" >> "$OUT"
echo "$(date +%H:%M:%S) Verbindung weg -- neuer Versuch in 2 s" >> "${OUT%.csv}.stderr.log"
sleep 2
done